142 lines
4.6 KiB
Python
142 lines
4.6 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from cloud.auth import NullAuthProvider, RejectingAuthProvider, create_auth_provider
|
|
from cloud.control_config import (
|
|
CloudConfigurationError,
|
|
CloudControlConfig,
|
|
load_control_config,
|
|
)
|
|
|
|
|
|
def test_load_control_config_uses_safe_local_defaults() -> None:
|
|
assert load_control_config({}) == CloudControlConfig()
|
|
|
|
|
|
def test_production_configuration_needs_no_static_credential_json() -> None:
|
|
config = load_control_config(
|
|
{
|
|
"CLOUD_ENVIRONMENT": "production",
|
|
"CLOUD_DATABASE_URL": "postgresql+psycopg://cloud@db/cloud",
|
|
"CLOUD_SCHEDULER_INTERVAL_SECONDS": "2.5",
|
|
"CLOUD_LEASE_REAPER_INTERVAL_SECONDS": "7",
|
|
"CLOUD_LEASE_DURATION_SECONDS": "90",
|
|
"CLOUD_MAX_TASK_ATTEMPTS": "5",
|
|
}
|
|
)
|
|
|
|
assert config.environment == "production"
|
|
assert config.database_url.startswith("postgresql+psycopg://")
|
|
assert config.scheduler_interval_seconds == 2.5
|
|
assert config.max_task_attempts == 5
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"environment,database_url",
|
|
[("invalid", "sqlite:///test.db"), ("local", "mysql://db/cloud")],
|
|
)
|
|
def test_load_control_config_rejects_invalid_environment_or_database(
|
|
environment: str,
|
|
database_url: str,
|
|
) -> None:
|
|
with pytest.raises(CloudConfigurationError):
|
|
load_control_config(
|
|
{
|
|
"CLOUD_ENVIRONMENT": environment,
|
|
"CLOUD_DATABASE_URL": database_url,
|
|
}
|
|
)
|
|
|
|
|
|
def test_load_control_config_rejects_unsafe_production_anonymous_mode() -> None:
|
|
with pytest.raises(CloudConfigurationError, match="production"):
|
|
load_control_config(
|
|
{
|
|
"CLOUD_ENVIRONMENT": "production",
|
|
"CLOUD_DATABASE_URL": "postgresql://db/cloud",
|
|
"CLOUD_ALLOW_INSECURE_ANONYMOUS": "true",
|
|
}
|
|
)
|
|
|
|
|
|
def test_load_control_config_parses_user_session_settings() -> None:
|
|
config = load_control_config(
|
|
{
|
|
"CLOUD_USER_SESSION_IDLE_SECONDS": "600",
|
|
"CLOUD_USER_SESSION_ABSOLUTE_SECONDS": "1200",
|
|
"CLOUD_LOGIN_FAILURE_LIMIT": "3",
|
|
"CLOUD_LOGIN_FAILURE_WINDOW_SECONDS": "60",
|
|
"CLOUD_LOGIN_BLOCK_SECONDS": "90",
|
|
"CLOUD_SESSION_COOKIE_SECURE": "true",
|
|
"CLOUD_TRUST_PROXY_HEADERS": "true",
|
|
}
|
|
)
|
|
|
|
assert config.user_session_idle_seconds == 600
|
|
assert config.user_session_absolute_seconds == 1200
|
|
assert config.login_failure_limit == 3
|
|
assert config.login_block_seconds == 90
|
|
assert config.session_cookie_secure is True
|
|
assert config.trust_proxy_headers is True
|
|
|
|
|
|
def test_load_control_config_rejects_unsafe_user_session_ttls() -> None:
|
|
with pytest.raises(CloudConfigurationError, match="ABSOLUTE"):
|
|
load_control_config(
|
|
{
|
|
"CLOUD_USER_SESSION_IDLE_SECONDS": "1200",
|
|
"CLOUD_USER_SESSION_ABSOLUTE_SECONDS": "600",
|
|
}
|
|
)
|
|
|
|
|
|
def test_production_requires_secure_user_session_cookie() -> None:
|
|
with pytest.raises(CloudConfigurationError, match="secure user session"):
|
|
load_control_config(
|
|
{
|
|
"CLOUD_ENVIRONMENT": "production",
|
|
"CLOUD_DATABASE_URL": "postgresql://db/cloud",
|
|
"CLOUD_SESSION_COOKIE_SECURE": "false",
|
|
}
|
|
)
|
|
|
|
|
|
def test_removed_static_credential_variables_are_ignored() -> None:
|
|
config = load_control_config(
|
|
{
|
|
"CLOUD_PUBLIC_CREDENTIALS_JSON": "not-json",
|
|
"CLOUD_HOST_CREDENTIALS_JSON": "not-json",
|
|
"CLOUD_ENROLLMENT_TOKENS_JSON": "not-json",
|
|
"CLOUD_SELF_SERVICE_ENROLLMENT_ENABLED": "not-a-bool",
|
|
}
|
|
)
|
|
|
|
assert config == CloudControlConfig()
|
|
|
|
|
|
def test_auth_provider_requires_explicit_anonymous_override() -> None:
|
|
secure_provider = create_auth_provider(allow_insecure_anonymous=False)
|
|
insecure_provider = create_auth_provider(allow_insecure_anonymous=True)
|
|
|
|
assert isinstance(secure_provider, RejectingAuthProvider)
|
|
request = type("Request", (), {"headers": {}})()
|
|
assert secure_provider.authenticate(request) is None
|
|
assert isinstance(insecure_provider, NullAuthProvider)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"name,value",
|
|
[
|
|
("CLOUD_SCHEDULER_INTERVAL_SECONDS", "0"),
|
|
("CLOUD_LEASE_DURATION_SECONDS", "invalid"),
|
|
("CLOUD_MAX_TASK_ATTEMPTS", "-1"),
|
|
],
|
|
)
|
|
def test_load_control_config_rejects_invalid_numeric_values(
|
|
name: str,
|
|
value: str,
|
|
) -> None:
|
|
with pytest.raises(CloudConfigurationError):
|
|
load_control_config({name: value})
|