Files
sales-trainer/backend/tests/test_bootstrap_config.py

160 lines
4.3 KiB
Python

"""Regression tests for fail-closed bootstrap and production auth configuration."""
from __future__ import annotations
import pytest
def _configure(
monkeypatch,
tmp_path,
*,
app_env: str,
secret: str,
bootstrap: str,
debug: bool | None = None,
):
from app.config import Config
monkeypatch.setattr(Config, "APP_ENV", app_env)
monkeypatch.setattr(Config, "DATA_DIR", tmp_path)
monkeypatch.setattr(Config, "SECRET_KEY", secret)
monkeypatch.setattr(Config, "BOOTSTRAP_ADMIN_PASSWORD", bootstrap)
monkeypatch.setattr(
Config, "FLASK_DEBUG", app_env != "production" if debug is None else debug
)
return Config
def test_production_empty_store_requires_bootstrap_password(monkeypatch, tmp_path):
config = _configure(
monkeypatch,
tmp_path,
app_env="production",
secret="production-secret-0123456789abcdef0123456789abcdef",
bootstrap="",
)
from app.factory import create_app
with pytest.raises(RuntimeError, match="BOOTSTRAP_ADMIN_PASSWORD"):
create_app()
def test_production_rejects_default_jwt_secret(monkeypatch, tmp_path):
_configure(
monkeypatch,
tmp_path,
app_env="production",
secret="short-invalid-secret",
bootstrap="bootstrap-password-0123456789",
)
from app.factory import create_app
with pytest.raises(RuntimeError, match="JWT_SECRET"):
create_app()
def test_debug_off_cannot_downgrade_runtime_to_development(
monkeypatch, tmp_path
):
_configure(
monkeypatch,
tmp_path,
app_env="development",
debug=False,
secret="short-secret",
bootstrap="bootstrap-password-0123456789",
)
from app.factory import create_app
with pytest.raises(RuntimeError, match="JWT_SECRET"):
create_app()
def test_debug_off_requires_strong_bootstrap_password(
monkeypatch, tmp_path
):
_configure(
monkeypatch,
tmp_path,
app_env="development",
debug=False,
secret="production-secret-0123456789abcdef0123456789abcdef",
bootstrap="short",
)
from app.factory import create_app
with pytest.raises(RuntimeError, match="BOOTSTRAP_ADMIN_PASSWORD"):
create_app()
def test_existing_user_store_does_not_need_bootstrap_password(
monkeypatch, tmp_path
):
from app.auth.users import UserStore
seed_store = UserStore(tmp_path)
seed_store.create_org("Existing Organization", org_id="org-default")
seed_store.create_user(
org_id="org-default",
username="existing-admin",
password="existing-password",
name="Existing Admin",
role="super_admin",
must_setup=False,
)
_configure(
monkeypatch,
tmp_path,
app_env="production",
secret="production-secret-0123456789abcdef0123456789abcdef",
bootstrap="",
)
from app.factory import create_app
app = create_app()
assert app.extensions["user_store"].get_user("existing-admin")["role"] == "super_admin"
def test_test_mode_uses_injected_bootstrap_password(monkeypatch, tmp_path):
_configure(
monkeypatch,
tmp_path,
app_env="test",
secret="pytest-secret-0123456789abcdef0123456789abcdef",
bootstrap="pytest-bootstrap-password",
)
from app.factory import create_app
app = create_app()
user_store = app.extensions["user_store"]
assert user_store.get_user("admin")["must_setup"] is True
def test_default_runtime_environment_is_fail_closed(monkeypatch):
from app.config import Config
monkeypatch.delenv("APP_ENV", raising=False)
monkeypatch.delenv("FLASK_DEBUG", raising=False)
monkeypatch.setattr(Config, "APP_ENV", "production")
monkeypatch.setattr(Config, "FLASK_DEBUG", False)
monkeypatch.setattr(Config, "SECRET_KEY", "")
with pytest.raises(RuntimeError, match="JWT_SECRET"):
Config.validate_runtime_security()
def test_known_bootstrap_placeholder_is_rejected(monkeypatch, tmp_path):
_configure(
monkeypatch,
tmp_path,
app_env="production",
secret="production-secret-0123456789abcdef0123456789abcdef",
bootstrap="replace_with_a_strong_initial_password",
)
from app.factory import create_app
with pytest.raises(RuntimeError, match="BOOTSTRAP_ADMIN_PASSWORD"):
create_app()