2026-03-28 00:45:43 +00:00
|
|
|
"""Tests for warden.config."""
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
|
|
from warden.config import ConfigError, load_config
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def write_yaml(path: Path, content: dict) -> None:
|
|
|
|
|
with path.open("w") as f:
|
|
|
|
|
yaml.dump(content, f)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_load_local_config(tmp_path):
|
|
|
|
|
cfg_path = tmp_path / "warden.yaml"
|
|
|
|
|
write_yaml(cfg_path, {"backend": "local", "ca_key": str(tmp_path / "ca")})
|
|
|
|
|
cfg = load_config(cfg_path)
|
|
|
|
|
assert cfg.backend == "local"
|
|
|
|
|
assert cfg.ca_key == tmp_path / "ca"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_local_backend_missing_ca_key_raises(tmp_path):
|
|
|
|
|
cfg_path = tmp_path / "warden.yaml"
|
|
|
|
|
write_yaml(cfg_path, {"backend": "local"})
|
|
|
|
|
with pytest.raises(ConfigError, match="ca_key"):
|
|
|
|
|
load_config(cfg_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_invalid_backend_raises(tmp_path):
|
|
|
|
|
cfg_path = tmp_path / "warden.yaml"
|
|
|
|
|
write_yaml(cfg_path, {"backend": "magic", "ca_key": "/tmp/ca"})
|
|
|
|
|
with pytest.raises(ConfigError, match="backend"):
|
|
|
|
|
load_config(cfg_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_vault_backend(tmp_path):
|
|
|
|
|
cfg_path = tmp_path / "warden.yaml"
|
|
|
|
|
write_yaml(cfg_path, {
|
|
|
|
|
"backend": "vault",
|
|
|
|
|
"vault": {
|
|
|
|
|
"addr": "https://vault.example.com",
|
|
|
|
|
"role_map": {"adm": "adm-role", "agt": "agt-role", "atm": "atm-role"},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
cfg = load_config(cfg_path)
|
|
|
|
|
assert cfg.backend == "vault"
|
|
|
|
|
assert cfg.vault is not None
|
|
|
|
|
assert cfg.vault.addr == "https://vault.example.com"
|
|
|
|
|
assert cfg.vault.role_map["agt"] == "agt-role"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_vault_backend_missing_addr_raises(tmp_path):
|
|
|
|
|
cfg_path = tmp_path / "warden.yaml"
|
|
|
|
|
write_yaml(cfg_path, {"backend": "vault", "vault": {}})
|
|
|
|
|
with pytest.raises(ConfigError, match="addr"):
|
|
|
|
|
load_config(cfg_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_missing_config_raises():
|
|
|
|
|
with pytest.raises(ConfigError, match="not found"):
|
|
|
|
|
load_config(Path("/nonexistent/path/warden.yaml"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_custom_state_dir(tmp_path):
|
|
|
|
|
cfg_path = tmp_path / "warden.yaml"
|
|
|
|
|
custom_state = tmp_path / "my-state"
|
|
|
|
|
write_yaml(cfg_path, {
|
|
|
|
|
"backend": "local",
|
|
|
|
|
"ca_key": str(tmp_path / "ca"),
|
|
|
|
|
"state_dir": str(custom_state),
|
|
|
|
|
})
|
|
|
|
|
cfg = load_config(cfg_path)
|
|
|
|
|
assert cfg.state_dir == custom_state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_default_vault_token_env(tmp_path):
|
|
|
|
|
cfg_path = tmp_path / "warden.yaml"
|
|
|
|
|
write_yaml(cfg_path, {
|
|
|
|
|
"backend": "vault",
|
|
|
|
|
"vault": {"addr": "https://vault.example.com"},
|
|
|
|
|
})
|
|
|
|
|
cfg = load_config(cfg_path)
|
|
|
|
|
assert cfg.vault.token_env == "VAULT_TOKEN"
|
2026-06-17 08:37:14 +02:00
|
|
|
|
|
|
|
|
|
2026-08-22 15:36:37 +02:00
|
|
|
def test_policy_defaults_to_unknown_zone_profile(tmp_path):
|
2026-06-17 08:37:14 +02:00
|
|
|
cfg_path = tmp_path / "warden.yaml"
|
|
|
|
|
write_yaml(cfg_path, {"backend": "local", "ca_key": str(tmp_path / "ca")})
|
|
|
|
|
cfg = load_config(cfg_path)
|
2026-08-22 15:36:37 +02:00
|
|
|
assert cfg.policy.flex_auth_url is None
|
|
|
|
|
assert cfg.policy.failure_modes["unknown"] == "fail_open"
|
|
|
|
|
assert cfg.policy.failure_modes["z3-critical"] == "fail_closed"
|
2026-06-17 08:37:14 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_policy_block_parsed(tmp_path):
|
|
|
|
|
cfg_path = tmp_path / "warden.yaml"
|
|
|
|
|
write_yaml(cfg_path, {
|
|
|
|
|
"backend": "local",
|
|
|
|
|
"ca_key": str(tmp_path / "ca"),
|
|
|
|
|
"policy": {
|
|
|
|
|
"flex_auth_url": "http://flex-auth:8080",
|
2026-08-22 15:36:37 +02:00
|
|
|
"zone_registry_path": str(tmp_path / "zones.json"),
|
|
|
|
|
"failure_modes": {"z2-protected": "fail_closed"},
|
2026-06-17 08:37:14 +02:00
|
|
|
"tenant": "tenant:coulomb",
|
|
|
|
|
"subject_env": "MY_SUBJECT",
|
|
|
|
|
"system": "warden-test",
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
cfg = load_config(cfg_path)
|
|
|
|
|
assert cfg.policy.flex_auth_url == "http://flex-auth:8080"
|
2026-08-22 15:36:37 +02:00
|
|
|
assert cfg.policy.zone_registry_path == tmp_path / "zones.json"
|
|
|
|
|
assert cfg.policy.failure_modes["z2-protected"] == "fail_closed"
|
2026-06-17 08:37:14 +02:00
|
|
|
assert cfg.policy.tenant == "tenant:coulomb"
|
|
|
|
|
assert cfg.policy.subject_env == "MY_SUBJECT"
|
|
|
|
|
assert cfg.policy.system == "warden-test"
|
2026-08-22 15:36:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("retired", ["enabled", "fail_closed"])
|
|
|
|
|
def test_retired_global_policy_switches_are_rejected(tmp_path, retired):
|
|
|
|
|
cfg_path = tmp_path / "warden.yaml"
|
|
|
|
|
write_yaml(cfg_path, {
|
|
|
|
|
"backend": "local",
|
|
|
|
|
"ca_key": str(tmp_path / "ca"),
|
|
|
|
|
"policy": {retired: True},
|
|
|
|
|
})
|
|
|
|
|
with pytest.raises(ConfigError, match=f"policy.{retired}"):
|
|
|
|
|
load_config(cfg_path)
|