diff --git a/example-configurations/bloodhound-enterprise/.dlt-example/config.toml b/example-configurations/bloodhound-enterprise/.dlt-example/config.toml index 8da76f80..0cdc72dd 100644 --- a/example-configurations/bloodhound-enterprise/.dlt-example/config.toml +++ b/example-configurations/bloodhound-enterprise/.dlt-example/config.toml @@ -1,4 +1,7 @@ # Example configuration: https://bloodhound.specterops.io/openhound/enterprise#full-configuration-example +[openhound] +managed = false + [runtime] http_show_error_body = true log_cli_level = "WARNING" diff --git a/src/openhound/config.py b/src/openhound/config.py new file mode 100644 index 00000000..b826c89a --- /dev/null +++ b/src/openhound/config.py @@ -0,0 +1,32 @@ +"""OpenHound managed-mode configuration.""" + +from dataclasses import dataclass + +import dlt + + +@dataclass(frozen=True) +class ModeDefaults: + """Placeholder for defaults that differ between OpenHound modes.""" + + +_UNMANAGED_DEFAULTS = ModeDefaults() +_MANAGED_DEFAULTS = ModeDefaults() + + +def is_managed() -> bool: + """Return whether OpenHound is configured to run in managed mode. + + The value is resolved on every call so all platform code observes the current + DLT configuration. Missing configuration defaults to unmanaged mode. Invalid + boolean values raise DLT's configuration coercion error. + """ + + configured = dlt.config.get("openhound.managed", bool) + return configured if configured is not None else False + + +def get_mode_defaults() -> ModeDefaults: + """Return the defaults profile for the configured mode.""" + + return _MANAGED_DEFAULTS if is_managed() else _UNMANAGED_DEFAULTS diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 00000000..3b632e87 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,45 @@ +import dlt +import pytest +from dlt.common.configuration.exceptions import ConfigValueCannotBeCoercedException + +from openhound.config import ModeDefaults, get_mode_defaults, is_managed + + +def test_is_managed_defaults_to_unmanaged(monkeypatch): + monkeypatch.setattr(dlt.config, "get", lambda field, expected_type: None) + + assert is_managed() is False + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + ("true", True), + ("false", False), + ], +) +def test_is_managed_resolves_environment_configuration(monkeypatch, value, expected): + monkeypatch.setenv("OPENHOUND__MANAGED", value) + + assert is_managed() is expected + + +def test_is_managed_rejects_invalid_boolean(monkeypatch): + monkeypatch.setenv("OPENHOUND__MANAGED", "maybe") + + with pytest.raises(ConfigValueCannotBeCoercedException): + is_managed() + + +def test_get_mode_defaults_selects_distinct_empty_profiles(monkeypatch): + monkeypatch.setattr("openhound.config.is_managed", lambda: False) + unmanaged = get_mode_defaults() + + monkeypatch.setattr("openhound.config.is_managed", lambda: True) + managed = get_mode_defaults() + + assert isinstance(unmanaged, ModeDefaults) + assert isinstance(managed, ModeDefaults) + assert unmanaged is not managed + assert vars(unmanaged) == {} + assert vars(managed) == {}