feat(release): publish minimal self-contained Magent source
This commit is contained in:
@@ -0,0 +1,519 @@
|
||||
"""Managed installation regression tests; use only disposable local files."""
|
||||
|
||||
import base64
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from contextlib import closing, redirect_stderr, redirect_stdout
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sqlite3
|
||||
import stat
|
||||
import tempfile
|
||||
from threading import Barrier
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from backend.app import container_bootstrap as bootstrap
|
||||
|
||||
|
||||
class ContainerBootstrapTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
temporary = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(temporary.cleanup)
|
||||
self.root = Path(temporary.name)
|
||||
self.data = self.root / "data"
|
||||
self.data.mkdir(mode=0o700)
|
||||
self.state_path = self.data / bootstrap.STATE_FILENAME
|
||||
self.database = self.data / "magent.db"
|
||||
self.environment = {
|
||||
"MAGENT_MANAGED_SECRETS": "true",
|
||||
"MAGENT_APPLICATION_URL": "https://magent.example.test",
|
||||
}
|
||||
|
||||
def prepare(self, **changes):
|
||||
return bootstrap.prepare_environment({**self.environment, **changes}, self.data)
|
||||
|
||||
def state(self):
|
||||
return json.loads(self.state_path.read_text(encoding="utf-8"))
|
||||
|
||||
def create_database(self, *, completed=0, admin=False):
|
||||
with closing(sqlite3.connect(self.database)) as connection:
|
||||
with connection:
|
||||
connection.execute("CREATE TABLE installation_setup (id INTEGER PRIMARY KEY, completed INTEGER)")
|
||||
connection.execute("INSERT INTO installation_setup VALUES (1, ?)", (completed,))
|
||||
connection.execute("CREATE TABLE users (role TEXT)")
|
||||
if admin:
|
||||
connection.execute("INSERT INTO users VALUES ('ADMIN')")
|
||||
|
||||
def create_symlink(self, path, target, *, directory=False):
|
||||
try:
|
||||
path.symlink_to(target, target_is_directory=directory)
|
||||
except (OSError, NotImplementedError) as exc:
|
||||
self.skipTest(f"This platform cannot create test symlinks: {type(exc).__name__}")
|
||||
|
||||
def test_fresh_install_generates_independent_valid_random_secrets(self):
|
||||
before = dict(self.environment)
|
||||
prepared = self.prepare()
|
||||
state = self.state()
|
||||
self.assertEqual(self.environment, before)
|
||||
self.assertEqual(set(state), {"version", *bootstrap.SECRET_NAMES})
|
||||
self.assertEqual(state["version"], 1)
|
||||
for name in ("JWT_SECRET", "SETUP_TOKEN"):
|
||||
self.assertRegex(state[name], r"^[A-Za-z0-9_-]{64}$")
|
||||
self.assertNotEqual(state["JWT_SECRET"], state["SETUP_TOKEN"])
|
||||
self.assertEqual(len(base64.urlsafe_b64decode(state["SETTINGS_ENCRYPTION_KEY"])), 32)
|
||||
for name in bootstrap.SECRET_NAMES:
|
||||
self.assertEqual(prepared[name], state[name])
|
||||
self.assertEqual(prepared["SQLITE_PATH"], str(self.database.absolute()))
|
||||
self.assertFalse(self.database.exists())
|
||||
self.assertEqual(list(self.data.glob(".magent-secrets-*")), [])
|
||||
|
||||
@unittest.skipUnless(os.name == "posix", "POSIX filesystem ownership/permissions")
|
||||
def test_state_has_private_permissions_and_runtime_ownership(self):
|
||||
self.prepare()
|
||||
metadata = self.state_path.stat()
|
||||
self.assertEqual(stat.S_IMODE(metadata.st_mode), 0o600)
|
||||
self.assertEqual(metadata.st_uid, os.geteuid())
|
||||
|
||||
def test_separate_installations_get_different_secrets(self):
|
||||
first = self.prepare()
|
||||
other = self.root / "other"
|
||||
other.mkdir(mode=0o700)
|
||||
second = bootstrap.prepare_environment(self.environment, other)
|
||||
for name in bootstrap.SECRET_NAMES:
|
||||
self.assertNotEqual(first[name], second[name])
|
||||
|
||||
def test_restart_and_existing_database_reuse_exact_file_and_values(self):
|
||||
first = self.prepare()
|
||||
original = self.state_path.read_bytes()
|
||||
original_modified = self.state_path.stat().st_mtime_ns
|
||||
self.create_database(admin=True)
|
||||
with patch.object(bootstrap.secrets, "token_bytes", side_effect=AssertionError("Must not regenerate")), \
|
||||
patch.object(bootstrap.secrets, "token_urlsafe", side_effect=AssertionError("Must not regenerate")):
|
||||
second = self.prepare()
|
||||
self.assertEqual(first, second)
|
||||
self.assertEqual(self.state_path.read_bytes(), original)
|
||||
self.assertEqual(self.state_path.stat().st_mtime_ns, original_modified)
|
||||
|
||||
def test_disabled_mode_is_an_unchanged_copy_without_filesystem_access(self):
|
||||
for value in (None, "false", "0", "no", "", " FALSE "):
|
||||
with self.subTest(mode=value):
|
||||
environment = {"JWT_SECRET": "legacy-key", "MAGENT_APPLICATION_URL": "invalid"}
|
||||
if value is not None:
|
||||
environment["MAGENT_MANAGED_SECRETS"] = value
|
||||
result = bootstrap.prepare_environment(environment, self.root / "does-not-exist")
|
||||
self.assertEqual(result, environment)
|
||||
self.assertIsNot(result, environment)
|
||||
self.assertFalse(self.state_path.exists())
|
||||
|
||||
def test_invalid_managed_mode_fails_before_writing(self):
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare(MAGENT_MANAGED_SECRETS="perhaps")
|
||||
self.assertFalse(self.state_path.exists())
|
||||
|
||||
def test_auto_mode_generates_fresh_install_keys_without_explicit_jwt(self):
|
||||
prepared = bootstrap.prepare_environment({"MAGENT_MANAGED_SECRETS": "auto"}, self.data)
|
||||
self.assertTrue(self.state_path.exists())
|
||||
self.assertEqual(prepared["MAGENT_MANAGED_SECRETS"], "true")
|
||||
self.assertEqual(prepared["MAGENT_RUNTIME_MANAGED"], "1")
|
||||
for name in bootstrap.SECRET_NAMES:
|
||||
self.assertEqual(prepared[name], self.state()[name])
|
||||
|
||||
def test_auto_mode_preserves_explicit_jwt_manual_install_without_filesystem_access(self):
|
||||
environment = {
|
||||
"MAGENT_MANAGED_SECRETS": "auto",
|
||||
"JWT_SECRET": "legacy-explicit-signing-key",
|
||||
"SQLITE_PATH": "/existing/custom-database.db",
|
||||
"API_DOCS_ENABLED": "true",
|
||||
"MAGENT_APPLICATION_URL": "https://legacy.example.test",
|
||||
"CORS_ALLOW_ORIGIN": "https://legacy.example.test",
|
||||
}
|
||||
prepared = bootstrap.prepare_environment(environment, self.root / "does-not-exist")
|
||||
self.assertEqual(prepared, environment)
|
||||
self.assertIsNot(prepared, environment)
|
||||
self.assertNotIn("SETTINGS_ENCRYPTION_KEY", prepared)
|
||||
self.assertNotIn("MAGENT_RUNTIME_MANAGED", prepared)
|
||||
self.assertFalse(self.state_path.exists())
|
||||
|
||||
def test_auto_mode_whitespace_jwt_is_treated_as_unset(self):
|
||||
prepared = bootstrap.prepare_environment({"MAGENT_MANAGED_SECRETS": "auto", "JWT_SECRET": " "}, self.data)
|
||||
self.assertEqual(prepared["JWT_SECRET"], self.state()["JWT_SECRET"])
|
||||
|
||||
def test_absent_application_url_uses_fixed_defaults_without_claiming_an_origin(self):
|
||||
prepared = bootstrap.prepare_environment({"MAGENT_MANAGED_SECRETS": "auto"}, self.data)
|
||||
self.assertFalse(prepared.get("MAGENT_APPLICATION_URL"))
|
||||
self.assertEqual(prepared["CORS_ALLOW_ORIGIN"], "http://localhost:3000")
|
||||
self.assertEqual(prepared["AUTH_COOKIE_SECURE"], "false")
|
||||
self.assertEqual(prepared["API_DOCS_ENABLED"], "false")
|
||||
self.assertEqual(prepared["SQLITE_PATH"], str(self.database.absolute()))
|
||||
|
||||
def test_empty_application_url_is_deferred_to_setup(self):
|
||||
prepared = self.prepare(MAGENT_APPLICATION_URL="")
|
||||
self.assertEqual(prepared["MAGENT_APPLICATION_URL"], "")
|
||||
self.assertEqual(prepared["CORS_ALLOW_ORIGIN"], "http://localhost:3000")
|
||||
self.assertTrue(self.state_path.exists())
|
||||
|
||||
def test_managed_api_docs_cannot_be_enabled(self):
|
||||
for value in ("true", "1", "yes", "on", "invalid"):
|
||||
with self.subTest(value=value), self.assertRaisesRegex(bootstrap.BootstrapError, "API_DOCS_ENABLED"):
|
||||
self.prepare(API_DOCS_ENABLED=value)
|
||||
self.assertFalse(self.state_path.exists())
|
||||
|
||||
def test_saved_public_url_controls_restart_without_key_regeneration(self):
|
||||
original = bootstrap.prepare_environment({"MAGENT_MANAGED_SECRETS": "auto"}, self.data)
|
||||
state_bytes = self.state_path.read_bytes()
|
||||
self.create_database(admin=True)
|
||||
with closing(sqlite3.connect(self.database)) as connection:
|
||||
with connection:
|
||||
connection.execute("CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT)")
|
||||
connection.execute("INSERT INTO settings VALUES ('magent_application_url', 'https://saved.example.test')")
|
||||
restarted = bootstrap.prepare_environment({"MAGENT_MANAGED_SECRETS": "auto"}, self.data)
|
||||
self.assertEqual(restarted["MAGENT_APPLICATION_URL"], "https://saved.example.test")
|
||||
self.assertEqual(restarted["CORS_ALLOW_ORIGIN"], "https://saved.example.test")
|
||||
self.assertEqual(restarted["AUTH_COOKIE_SECURE"], "true")
|
||||
self.assertEqual(self.state_path.read_bytes(), state_bytes)
|
||||
for name in bootstrap.SECRET_NAMES:
|
||||
self.assertEqual(restarted[name], original[name])
|
||||
|
||||
def test_saved_public_url_wins_over_stale_deployment_url_on_restart(self):
|
||||
self.prepare()
|
||||
self.create_database(admin=True)
|
||||
with closing(sqlite3.connect(self.database)) as connection:
|
||||
with connection:
|
||||
connection.execute("CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT)")
|
||||
connection.execute("INSERT INTO settings VALUES ('magent_application_url', 'http://magent.lan:3000')")
|
||||
restarted = self.prepare(CORS_ALLOW_ORIGIN="https://magent.example.test")
|
||||
self.assertEqual(restarted["MAGENT_APPLICATION_URL"], "http://magent.lan:3000")
|
||||
self.assertEqual(restarted["CORS_ALLOW_ORIGIN"], "http://magent.lan:3000")
|
||||
self.assertEqual(restarted["AUTH_COOKIE_SECURE"], "false")
|
||||
|
||||
def test_invalid_saved_url_fails_closed_without_changing_keys(self):
|
||||
self.prepare()
|
||||
original = self.state_path.read_bytes()
|
||||
self.create_database(admin=True)
|
||||
with closing(sqlite3.connect(self.database)) as connection:
|
||||
with connection:
|
||||
connection.execute("CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT)")
|
||||
connection.execute("INSERT INTO settings VALUES ('magent_application_url', 'https://user:secret@evil.test')")
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare()
|
||||
self.assertEqual(self.state_path.read_bytes(), original)
|
||||
|
||||
def test_existing_database_or_recovery_sidecar_never_generates_replacement_keys(self):
|
||||
for suffix in ("", "-wal", "-shm", "-journal"):
|
||||
with self.subTest(suffix=suffix):
|
||||
path = Path(str(self.database) + suffix)
|
||||
path.write_bytes(b"existing installation data")
|
||||
try:
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare()
|
||||
self.assertEqual(path.read_bytes(), b"existing installation data")
|
||||
self.assertFalse(self.state_path.exists())
|
||||
finally:
|
||||
path.unlink()
|
||||
|
||||
def test_lost_keys_after_initialization_are_not_recreated(self):
|
||||
self.prepare()
|
||||
self.create_database()
|
||||
self.state_path.unlink()
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare()
|
||||
self.assertFalse(self.state_path.exists())
|
||||
|
||||
def test_fresh_manual_secrets_conflict_without_writing_state(self):
|
||||
for name in bootstrap.SECRET_NAMES:
|
||||
with self.subTest(name=name), self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare(**{name: "synthetic-manual-secret"})
|
||||
self.assertFalse(self.state_path.exists())
|
||||
|
||||
def test_matching_environment_values_are_accepted_but_conflicts_never_replace_file(self):
|
||||
first = self.prepare()
|
||||
original = self.state_path.read_bytes()
|
||||
keys = {name: first[name] for name in bootstrap.SECRET_NAMES}
|
||||
self.assertEqual(self.prepare(**keys), first)
|
||||
for name in bootstrap.SECRET_NAMES:
|
||||
with self.subTest(name=name), self.assertRaises(bootstrap.BootstrapError) as raised:
|
||||
self.prepare(**{name: "conflicting-private-value"})
|
||||
self.assertNotIn("conflicting-private-value", str(raised.exception))
|
||||
self.assertEqual(self.state_path.read_bytes(), original)
|
||||
|
||||
def test_custom_database_location_is_rejected_without_touching_it(self):
|
||||
custom = self.root / "other.db"
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare(SQLITE_PATH=str(custom))
|
||||
self.assertFalse(custom.exists())
|
||||
self.assertFalse(self.state_path.exists())
|
||||
|
||||
def test_missing_or_symlink_data_directory_is_rejected(self):
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
bootstrap.prepare_environment(self.environment, self.root / "missing")
|
||||
linked = self.root / "linked-data"
|
||||
self.create_symlink(linked, self.data, directory=True)
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
bootstrap.prepare_environment(self.environment, linked)
|
||||
self.assertFalse(self.state_path.exists())
|
||||
|
||||
@unittest.skipUnless(os.name == "posix", "POSIX filesystem permissions")
|
||||
def test_shared_writable_data_directory_is_rejected(self):
|
||||
self.data.chmod(0o777)
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare()
|
||||
self.assertFalse(self.state_path.exists())
|
||||
|
||||
def test_malformed_json_oversized_and_invalid_schema_never_get_replaced(self):
|
||||
self.prepare()
|
||||
valid = self.state()
|
||||
invalid_states = [
|
||||
b"not-json", b"\xff", b"x" * (bootstrap.MAX_STATE_BYTES + 1), b"[]", b"{}",
|
||||
json.dumps({**valid, "version": True}).encode(),
|
||||
json.dumps({**valid, "version": 2}).encode(),
|
||||
json.dumps({**valid, "unexpected": "value"}).encode(),
|
||||
json.dumps({**valid, "JWT_SECRET": None}).encode(),
|
||||
json.dumps({**valid, "JWT_SECRET": "a" * 64}).encode(),
|
||||
json.dumps({**valid, "JWT_SECRET": "short"}).encode(),
|
||||
json.dumps({**valid, "SETUP_TOKEN": valid["JWT_SECRET"]}).encode(),
|
||||
json.dumps({**valid, "SETTINGS_ENCRYPTION_KEY": "invalid-key"}).encode(),
|
||||
json.dumps({**valid, "SETTINGS_ENCRYPTION_KEY": base64.urlsafe_b64encode(b"short").decode()}).encode(),
|
||||
]
|
||||
for index, payload in enumerate(invalid_states):
|
||||
with self.subTest(case=index):
|
||||
self.state_path.write_bytes(payload)
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare()
|
||||
self.assertEqual(self.state_path.read_bytes(), payload)
|
||||
|
||||
def test_state_directory_is_not_replaced(self):
|
||||
self.state_path.mkdir()
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare()
|
||||
self.assertTrue(self.state_path.is_dir())
|
||||
|
||||
def test_state_symlink_is_not_followed_or_replaced(self):
|
||||
self.prepare()
|
||||
target = self.root / "original-secrets.json"
|
||||
self.state_path.rename(target)
|
||||
original = target.read_bytes()
|
||||
self.create_symlink(self.state_path, target)
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare()
|
||||
self.assertEqual(target.read_bytes(), original)
|
||||
self.assertTrue(self.state_path.is_symlink())
|
||||
|
||||
@unittest.skipUnless(os.name == "posix", "POSIX filesystem permissions")
|
||||
def test_publicly_readable_secrets_are_rejected_without_fixing_or_overwriting_them(self):
|
||||
self.prepare()
|
||||
original = self.state_path.read_bytes()
|
||||
self.state_path.chmod(0o644)
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare()
|
||||
self.assertEqual(stat.S_IMODE(self.state_path.stat().st_mode), 0o644)
|
||||
self.assertEqual(self.state_path.read_bytes(), original)
|
||||
|
||||
@unittest.skipUnless(hasattr(os, "mkfifo"), "POSIX named pipes")
|
||||
def test_named_pipe_state_is_rejected_without_blocking(self):
|
||||
os.mkfifo(self.state_path, 0o600)
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare()
|
||||
self.assertTrue(stat.S_ISFIFO(self.state_path.stat().st_mode))
|
||||
|
||||
def test_https_sets_matching_cors_and_secure_cookies(self):
|
||||
prepared = self.prepare()
|
||||
self.assertEqual(prepared["CORS_ALLOW_ORIGIN"], self.environment["MAGENT_APPLICATION_URL"])
|
||||
self.assertEqual(prepared["AUTH_COOKIE_SECURE"], "true")
|
||||
|
||||
def test_explicit_http_lan_origin_disables_secure_cookie_flag_only(self):
|
||||
prepared = self.prepare(MAGENT_APPLICATION_URL="http://192.0.2.10:3000")
|
||||
self.assertEqual(prepared["CORS_ALLOW_ORIGIN"], "http://192.0.2.10:3000")
|
||||
self.assertEqual(prepared["AUTH_COOKIE_SECURE"], "false")
|
||||
|
||||
def test_invalid_origin_fails_without_creating_keys(self):
|
||||
origins = (
|
||||
"not-a-url", "https://magent.example.test/", "https://magent.example.test/path",
|
||||
"//magent.example.test", "ftp://magent.example.test", "http:/magent.example.test",
|
||||
"https://user:password@magent.example.test", "https://@magent.example.test",
|
||||
"https://magent.example.test?", "https://magent.example.test#",
|
||||
"https://magent.example.test:0", "https://magent.example.test:65536",
|
||||
"https://*.example.test", "https://magent.\ttest", "https://magent.example.test\\path",
|
||||
" https://magent.example.test", "https://magent.example.test\x00",
|
||||
)
|
||||
for origin in origins:
|
||||
with self.subTest(origin=repr(origin)), self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare(MAGENT_APPLICATION_URL=origin)
|
||||
self.assertFalse(self.state_path.exists())
|
||||
|
||||
def test_cors_mismatch_or_cookie_scheme_conflict_fails_without_keys(self):
|
||||
cases = (
|
||||
{"CORS_ALLOW_ORIGIN": "https://elsewhere.example.test"},
|
||||
{"AUTH_COOKIE_SECURE": "false"},
|
||||
{"AUTH_COOKIE_SECURE": "0"},
|
||||
{"AUTH_COOKIE_SECURE": "maybe"},
|
||||
{"MAGENT_APPLICATION_URL": "http://magent.lan:3000", "AUTH_COOKIE_SECURE": "true"},
|
||||
{"MAGENT_APPLICATION_URL": "http://magent.lan:3000", "AUTH_COOKIE_SECURE": "1"},
|
||||
)
|
||||
for changes in cases:
|
||||
with self.subTest(changes=changes), self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare(**changes)
|
||||
self.assertFalse(self.state_path.exists())
|
||||
|
||||
def test_racing_initializers_publish_and_return_one_complete_state(self):
|
||||
barrier = Barrier(8)
|
||||
|
||||
def initialize(_):
|
||||
barrier.wait(timeout=10)
|
||||
return self.prepare()
|
||||
|
||||
with ThreadPoolExecutor(max_workers=8) as executor:
|
||||
results = list(executor.map(initialize, range(8)))
|
||||
for result in results:
|
||||
self.assertEqual(result, results[0])
|
||||
state = self.state()
|
||||
for name in bootstrap.SECRET_NAMES:
|
||||
self.assertEqual(state[name], results[0][name])
|
||||
self.assertEqual(list(self.data.glob(".magent-secrets-*")), [])
|
||||
|
||||
def test_token_command_requires_managed_mode_and_does_not_create_state(self):
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
bootstrap.setup_token({}, self.data)
|
||||
self.assertFalse(self.state_path.exists())
|
||||
with self.assertRaises((bootstrap.BootstrapError, FileNotFoundError)):
|
||||
bootstrap.setup_token(self.environment, self.data)
|
||||
self.assertFalse(self.state_path.exists())
|
||||
self.assertFalse(self.database.exists())
|
||||
|
||||
def test_token_command_does_not_create_an_uninitialized_database(self):
|
||||
self.prepare()
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
bootstrap.setup_token(self.environment, self.data)
|
||||
self.assertFalse(self.database.exists())
|
||||
|
||||
def test_token_command_returns_only_initial_token_using_readonly_closed_connection(self):
|
||||
prepared = self.prepare()
|
||||
self.create_database()
|
||||
before = {path.name: path.read_bytes() for path in self.data.iterdir()}
|
||||
connections = []
|
||||
real_connect = sqlite3.connect
|
||||
|
||||
def connect(*args, **kwargs):
|
||||
self.assertTrue(kwargs.get("uri"))
|
||||
self.assertTrue(args[0].endswith("?mode=ro"))
|
||||
connection = real_connect(*args, **kwargs)
|
||||
with self.assertRaises(sqlite3.OperationalError):
|
||||
connection.execute("INSERT INTO users VALUES ('admin')")
|
||||
connections.append(connection)
|
||||
return connection
|
||||
|
||||
with patch.object(bootstrap.sqlite3, "connect", side_effect=connect):
|
||||
token = bootstrap.setup_token(self.environment, self.data)
|
||||
self.assertEqual(token, prepared["SETUP_TOKEN"])
|
||||
self.assertEqual({path.name: path.read_bytes() for path in self.data.iterdir()}, before)
|
||||
for connection in connections:
|
||||
with self.assertRaises(sqlite3.ProgrammingError):
|
||||
connection.execute("SELECT 1")
|
||||
|
||||
def test_token_command_refuses_once_any_admin_exists_even_before_setup_completion(self):
|
||||
self.prepare()
|
||||
self.create_database(admin=True)
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
bootstrap.setup_token(self.environment, self.data)
|
||||
|
||||
def test_token_command_refuses_completed_setup_even_without_admin(self):
|
||||
self.prepare()
|
||||
self.create_database(completed=1)
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
bootstrap.setup_token(self.environment, self.data)
|
||||
|
||||
def test_token_command_refuses_unknown_or_invalid_database_state(self):
|
||||
self.prepare()
|
||||
for payload in (b"", b"not a SQLite database"):
|
||||
with self.subTest(payload=payload):
|
||||
self.database.write_bytes(payload)
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
bootstrap.setup_token(self.environment, self.data)
|
||||
self.assertEqual(self.database.read_bytes(), payload)
|
||||
self.database.unlink()
|
||||
self.create_database()
|
||||
with closing(sqlite3.connect(self.database)) as connection:
|
||||
with connection:
|
||||
connection.execute("DELETE FROM installation_setup")
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
bootstrap.setup_token(self.environment, self.data)
|
||||
|
||||
def test_existing_database_symlink_is_rejected_even_with_valid_state(self):
|
||||
self.prepare()
|
||||
self.create_database()
|
||||
target = self.root / "other.db"
|
||||
self.database.rename(target)
|
||||
original = target.read_bytes()
|
||||
self.create_symlink(self.database, target)
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare()
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
bootstrap.setup_token(self.environment, self.data)
|
||||
self.assertEqual(target.read_bytes(), original)
|
||||
|
||||
def test_existing_database_directory_is_rejected_even_with_valid_state(self):
|
||||
self.prepare()
|
||||
self.database.mkdir()
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
self.prepare()
|
||||
with self.assertRaises(bootstrap.BootstrapError):
|
||||
bootstrap.setup_token(self.environment, self.data)
|
||||
self.assertTrue(self.database.is_dir())
|
||||
|
||||
def test_startup_passes_keys_to_runtime_without_printing_them(self):
|
||||
prepared = self.prepare()
|
||||
stdout, stderr = io.StringIO(), io.StringIO()
|
||||
with patch.dict(os.environ, self.environment, clear=True), \
|
||||
patch.object(bootstrap.sys, "argv", ["bootstrap", "supervisord", "-c", "config"]), \
|
||||
patch.object(bootstrap, "prepare_environment", return_value=prepared), \
|
||||
patch.object(bootstrap.os, "execvpe") as execute, \
|
||||
redirect_stdout(stdout), redirect_stderr(stderr):
|
||||
self.assertEqual(bootstrap.main(), 0)
|
||||
execute.assert_called_once_with("supervisord", ["supervisord", "-c", "config"], prepared)
|
||||
self.assertIn("setup-token", stdout.getvalue())
|
||||
self.assertEqual(stderr.getvalue(), "")
|
||||
for name in bootstrap.SECRET_NAMES:
|
||||
self.assertNotIn(prepared[name], stdout.getvalue() + stderr.getvalue())
|
||||
|
||||
def test_disabled_startup_does_not_print_managed_install_instructions(self):
|
||||
stdout, stderr = io.StringIO(), io.StringIO()
|
||||
environment = {"JWT_SECRET": "manual-test-value"}
|
||||
with patch.dict(os.environ, environment, clear=True), \
|
||||
patch.object(bootstrap.sys, "argv", ["bootstrap", "supervisord"]), \
|
||||
patch.object(bootstrap.os, "execvpe") as execute, \
|
||||
redirect_stdout(stdout), redirect_stderr(stderr):
|
||||
self.assertEqual(bootstrap.main(), 0)
|
||||
execute.assert_called_once_with("supervisord", ["supervisord"], environment)
|
||||
self.assertEqual(stdout.getvalue() + stderr.getvalue(), "")
|
||||
|
||||
def test_cli_explicit_token_command_prints_only_token_not_other_keys(self):
|
||||
prepared = self.prepare()
|
||||
self.create_database()
|
||||
retrieve = bootstrap.setup_token
|
||||
stdout, stderr = io.StringIO(), io.StringIO()
|
||||
with patch.dict(os.environ, self.environment, clear=True), \
|
||||
patch.object(bootstrap.sys, "argv", ["bootstrap", "setup-token"]), \
|
||||
patch.object(bootstrap, "setup_token", side_effect=lambda env: retrieve(env, self.data)), \
|
||||
patch.object(bootstrap.os, "execvpe") as execute, \
|
||||
redirect_stdout(stdout), redirect_stderr(stderr):
|
||||
self.assertEqual(bootstrap.main(), 0)
|
||||
execute.assert_not_called()
|
||||
self.assertEqual(stdout.getvalue(), prepared["SETUP_TOKEN"] + "\n")
|
||||
self.assertEqual(stderr.getvalue(), "")
|
||||
self.assertNotIn(prepared["JWT_SECRET"], stdout.getvalue())
|
||||
self.assertNotIn(prepared["SETTINGS_ENCRYPTION_KEY"], stdout.getvalue())
|
||||
|
||||
def test_cli_unexpected_io_failure_never_logs_sensitive_exception_details(self):
|
||||
stdout, stderr = io.StringIO(), io.StringIO()
|
||||
with patch.object(bootstrap.sys, "argv", ["bootstrap", "supervisord"]), \
|
||||
patch.object(bootstrap, "prepare_environment", side_effect=OSError("private-secret-material")), \
|
||||
redirect_stdout(stdout), redirect_stderr(stderr):
|
||||
self.assertEqual(bootstrap.main(), 1)
|
||||
self.assertEqual(stdout.getvalue(), "")
|
||||
self.assertNotIn("private-secret-material", stderr.getvalue())
|
||||
self.assertIn("Check volume permissions", stderr.getvalue())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user