This commit is contained in:
@@ -7,15 +7,11 @@ import pytest
|
||||
from cloud.auth import (
|
||||
BearerCredential,
|
||||
ChainedAuthProvider,
|
||||
ChainedEnrollmentAuthProvider,
|
||||
ConfiguredBearerAuthProvider,
|
||||
ConfiguredEnrollmentTokenProvider,
|
||||
EnrollmentCredential,
|
||||
HostIdentityMismatchError,
|
||||
HostPrincipalRequiredError,
|
||||
NullAuthProvider,
|
||||
RepositoryHostAuthProvider,
|
||||
SelfServiceEnrollmentAuthProvider,
|
||||
digest_token,
|
||||
)
|
||||
|
||||
@@ -171,24 +167,6 @@ def test_authentication_failure_does_not_log_bearer_secret(caplog) -> None:
|
||||
assert "valid-secret" not in caplog.text
|
||||
|
||||
|
||||
def test_enrollment_token_provider_returns_digest_without_exposing_secret() -> None:
|
||||
credential = EnrollmentCredential(
|
||||
principal_id="installer-a",
|
||||
token="one-time-enrollment-secret",
|
||||
)
|
||||
provider = ConfiguredEnrollmentTokenProvider([credential])
|
||||
|
||||
principal = provider.authenticate(
|
||||
_Request(headers={"authorization": "Bearer one-time-enrollment-secret"})
|
||||
)
|
||||
|
||||
assert principal is not None
|
||||
assert principal.id == "installer-a"
|
||||
assert principal.token_digest == digest_token("one-time-enrollment-secret")
|
||||
assert "one-time-enrollment-secret" not in repr(credential)
|
||||
assert "one-time-enrollment-secret" not in repr(provider.__dict__)
|
||||
|
||||
|
||||
def test_repository_host_auth_and_chain_preserve_host_scope() -> None:
|
||||
class Repository:
|
||||
def authenticate_enrolled_host(self, credential_digest: str) -> str | None:
|
||||
@@ -215,67 +193,3 @@ def test_repository_host_auth_and_chain_preserve_host_scope() -> None:
|
||||
assert host_principal is not None
|
||||
assert host_principal.host_id == "host-managed"
|
||||
assert host_principal.scopes == frozenset()
|
||||
|
||||
|
||||
def test_self_service_enabled_with_no_token_enrolls_via_self_service() -> None:
|
||||
provider = ChainedEnrollmentAuthProvider(
|
||||
[ConfiguredEnrollmentTokenProvider(()), SelfServiceEnrollmentAuthProvider()]
|
||||
)
|
||||
|
||||
principal = provider.authenticate(_Request(headers={}))
|
||||
|
||||
assert principal is not None
|
||||
assert principal.id == "self-service"
|
||||
assert principal.token_digest is None
|
||||
|
||||
|
||||
def test_self_service_disabled_with_no_token_is_unauthenticated() -> None:
|
||||
provider = ChainedEnrollmentAuthProvider([ConfiguredEnrollmentTokenProvider(())])
|
||||
|
||||
assert provider.authenticate(_Request(headers={})) is None
|
||||
|
||||
|
||||
def test_self_service_enabled_with_valid_configured_token_uses_token_bound_path() -> (
|
||||
None
|
||||
):
|
||||
credential = EnrollmentCredential(
|
||||
principal_id="installer-a",
|
||||
token="one-time-enrollment-secret",
|
||||
)
|
||||
provider = ChainedEnrollmentAuthProvider(
|
||||
[
|
||||
ConfiguredEnrollmentTokenProvider([credential]),
|
||||
SelfServiceEnrollmentAuthProvider(),
|
||||
]
|
||||
)
|
||||
|
||||
principal = provider.authenticate(
|
||||
_Request(headers={"authorization": "Bearer one-time-enrollment-secret"})
|
||||
)
|
||||
|
||||
assert principal is not None
|
||||
assert principal.id == "installer-a"
|
||||
assert principal.token_digest == digest_token("one-time-enrollment-secret")
|
||||
|
||||
|
||||
def test_self_service_enabled_with_unknown_token_falls_through_to_self_service() -> (
|
||||
None
|
||||
):
|
||||
credential = EnrollmentCredential(
|
||||
principal_id="installer-a",
|
||||
token="one-time-enrollment-secret",
|
||||
)
|
||||
provider = ChainedEnrollmentAuthProvider(
|
||||
[
|
||||
ConfiguredEnrollmentTokenProvider([credential]),
|
||||
SelfServiceEnrollmentAuthProvider(),
|
||||
]
|
||||
)
|
||||
|
||||
principal = provider.authenticate(
|
||||
_Request(headers={"authorization": "Bearer unknown-secret"})
|
||||
)
|
||||
|
||||
assert principal is not None
|
||||
assert principal.id == "self-service"
|
||||
assert principal.token_digest is None
|
||||
|
||||
@@ -2,11 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from cloud.auth import (
|
||||
ConfiguredBearerAuthProvider,
|
||||
NullAuthProvider,
|
||||
create_auth_provider,
|
||||
)
|
||||
from cloud.auth import NullAuthProvider, RejectingAuthProvider, create_auth_provider
|
||||
from cloud.control_config import (
|
||||
CloudConfigurationError,
|
||||
CloudControlConfig,
|
||||
@@ -18,7 +14,7 @@ def test_load_control_config_uses_safe_local_defaults() -> None:
|
||||
assert load_control_config({}) == CloudControlConfig()
|
||||
|
||||
|
||||
def test_load_control_config_parses_deployment_values() -> None:
|
||||
def test_production_configuration_needs_no_static_credential_json() -> None:
|
||||
config = load_control_config(
|
||||
{
|
||||
"CLOUD_ENVIRONMENT": "production",
|
||||
@@ -27,14 +23,6 @@ def test_load_control_config_parses_deployment_values() -> None:
|
||||
"CLOUD_LEASE_REAPER_INTERVAL_SECONDS": "7",
|
||||
"CLOUD_LEASE_DURATION_SECONDS": "90",
|
||||
"CLOUD_MAX_TASK_ATTEMPTS": "5",
|
||||
"CLOUD_PUBLIC_CREDENTIALS_JSON": (
|
||||
'[{"principal_id":"sdk","token":"sdk-secret",'
|
||||
'"scopes":["tasks:submit","tasks:read"]}]'
|
||||
),
|
||||
"CLOUD_HOST_CREDENTIALS_JSON": (
|
||||
'[{"principal_id":"agent-a","token":"host-secret",'
|
||||
'"host_id":"host-a","scopes":["host:agent"]}]'
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -42,23 +30,6 @@ def test_load_control_config_parses_deployment_values() -> None:
|
||||
assert config.database_url.startswith("postgresql+psycopg://")
|
||||
assert config.scheduler_interval_seconds == 2.5
|
||||
assert config.max_task_attempts == 5
|
||||
assert len(config.credentials) == 2
|
||||
assert config.credentials[1].host_id == "host-a"
|
||||
assert "sdk-secret" not in repr(config)
|
||||
|
||||
|
||||
def test_load_control_config_parses_enrollment_credentials() -> None:
|
||||
config = load_control_config(
|
||||
{
|
||||
"CLOUD_ENROLLMENT_TOKENS_JSON": (
|
||||
'[{"principal_id":"installer-a","token":"one-time-enrollment-secret"}]'
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
assert len(config.enrollment_credentials) == 1
|
||||
assert config.enrollment_credentials[0].principal_id == "installer-a"
|
||||
assert "one-time-enrollment-secret" not in repr(config)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -85,19 +56,6 @@ def test_load_control_config_rejects_unsafe_production_anonymous_mode() -> None:
|
||||
"CLOUD_ENVIRONMENT": "production",
|
||||
"CLOUD_DATABASE_URL": "postgresql://db/cloud",
|
||||
"CLOUD_ALLOW_INSECURE_ANONYMOUS": "true",
|
||||
"CLOUD_PUBLIC_CREDENTIALS_JSON": (
|
||||
'[{"principal_id":"sdk","token":"secret","scopes":[]}]'
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_load_control_config_rejects_missing_production_credentials() -> None:
|
||||
with pytest.raises(CloudConfigurationError, match="credential"):
|
||||
load_control_config(
|
||||
{
|
||||
"CLOUD_ENVIRONMENT": "production",
|
||||
"CLOUD_DATABASE_URL": "postgresql://db/cloud",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -139,43 +97,29 @@ def test_production_requires_secure_user_session_cookie() -> None:
|
||||
{
|
||||
"CLOUD_ENVIRONMENT": "production",
|
||||
"CLOUD_DATABASE_URL": "postgresql://db/cloud",
|
||||
"CLOUD_PUBLIC_CREDENTIALS_JSON": (
|
||||
'[{"principal_id":"sdk","token":"secret","scopes":[]}]'
|
||||
),
|
||||
"CLOUD_SESSION_COOKIE_SECURE": "false",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name,value",
|
||||
[
|
||||
("CLOUD_PUBLIC_CREDENTIALS_JSON", "not-json"),
|
||||
("CLOUD_PUBLIC_CREDENTIALS_JSON", "{}"),
|
||||
(
|
||||
"CLOUD_HOST_CREDENTIALS_JSON",
|
||||
'[{"principal_id":"agent","token":"secret","scopes":[]}]',
|
||||
),
|
||||
(
|
||||
"CLOUD_ENROLLMENT_TOKENS_JSON",
|
||||
'[{"principal_id":"installer","token":123}]',
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_load_control_config_rejects_invalid_credentials(
|
||||
name: str,
|
||||
value: str,
|
||||
) -> None:
|
||||
with pytest.raises(CloudConfigurationError, match="credential") as error:
|
||||
load_control_config({name: value})
|
||||
assert "secret" not in str(error.value)
|
||||
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)
|
||||
secure_provider = create_auth_provider(allow_insecure_anonymous=False)
|
||||
insecure_provider = create_auth_provider(allow_insecure_anonymous=True)
|
||||
|
||||
assert isinstance(secure_provider, ConfiguredBearerAuthProvider)
|
||||
assert isinstance(secure_provider, RejectingAuthProvider)
|
||||
request = type("Request", (), {"headers": {}})()
|
||||
assert secure_provider.authenticate(request) is None
|
||||
assert isinstance(insecure_provider, NullAuthProvider)
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
REMOVED_CREDENTIAL_SETTINGS = {
|
||||
"CLOUD_PUBLIC_CREDENTIALS_JSON",
|
||||
"CLOUD_HOST_CREDENTIALS_JSON",
|
||||
"CLOUD_ENROLLMENT_TOKENS_JSON",
|
||||
"CLOUD_SELF_SERVICE_ENROLLMENT_ENABLED",
|
||||
"CLOUD_CONSOLE_STATIC_DIR",
|
||||
"HOST_AGENT_HOST_ID",
|
||||
"HOST_AGENT_TOKEN",
|
||||
"HOST_AGENT_ENROLLMENT_TOKEN",
|
||||
}
|
||||
|
||||
|
||||
def test_compose_defines_database_control_plane_and_outbound_host_agent() -> None:
|
||||
@@ -29,45 +38,57 @@ def test_compose_defines_database_control_plane_and_outbound_host_agent() -> Non
|
||||
services["host-agent"]["environment"]["HOST_AGENT_CONTROL_PLANE_URL"]
|
||||
== "http://cloud-api:8001"
|
||||
)
|
||||
assert services["host-agent"]["environment"]["AI_PLANNER_ENABLED"] == (
|
||||
"${AI_PLANNER_ENABLED:-false}"
|
||||
assert services["host-agent"]["environment"]["HOST_AGENT_IDENTITY_PATH"] == (
|
||||
"${HOST_AGENT_IDENTITY_PATH:-/app/tasks/host_identity.json}"
|
||||
)
|
||||
assert (
|
||||
services["cloud-api"]["environment"]["CLOUD_ENROLLMENT_TOKENS_JSON"]
|
||||
== "${CLOUD_ENROLLMENT_TOKENS_JSON:-[]}"
|
||||
)
|
||||
assert (
|
||||
services["host-agent"]["environment"]["HOST_AGENT_IDENTITY_PATH"]
|
||||
== "${HOST_AGENT_IDENTITY_PATH:-/app/tasks/host_identity.json}"
|
||||
)
|
||||
assert "ports" not in services["host-agent"]
|
||||
assert not (
|
||||
set(services["cloud-api"]["environment"])
|
||||
| set(services["host-agent"]["environment"])
|
||||
) & REMOVED_CREDENTIAL_SETTINGS
|
||||
|
||||
|
||||
def test_container_uses_locked_workspace_install_and_migrations() -> None:
|
||||
def test_deploy_compose_has_only_cloud_services_and_minimal_environment() -> None:
|
||||
compose = yaml.safe_load(
|
||||
(ROOT / "compose.deploy.yaml").read_text(encoding="utf-8")
|
||||
)
|
||||
services = compose["services"]
|
||||
|
||||
assert set(services) == {"postgres", "cloud-api"}
|
||||
assert services["cloud-api"]["image"].endswith(":${IMAGE_TAG:-latest}")
|
||||
assert services["cloud-api"]["environment"] == {
|
||||
"CLOUD_ENVIRONMENT": "production",
|
||||
"CLOUD_DATABASE_URL": (
|
||||
"postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}"
|
||||
"@postgres:5432/${POSTGRES_DB}"
|
||||
),
|
||||
"CLOUD_TRUST_PROXY_HEADERS": "${CLOUD_TRUST_PROXY_HEADERS:-false}",
|
||||
}
|
||||
|
||||
|
||||
def test_container_uses_locked_workspace_install_migrations_and_static_console() -> None:
|
||||
dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8")
|
||||
compose = yaml.safe_load((ROOT / "compose.yaml").read_text(encoding="utf-8"))
|
||||
cloud_command = compose["services"]["cloud-api"]["command"][-1]
|
||||
|
||||
assert "uv sync --locked --all-packages --no-dev" in dockerfile
|
||||
assert "CLOUD_CONSOLE_STATIC_DIR=/app/console-static" in dockerfile
|
||||
assert "alembic" in cloud_command
|
||||
assert "upgrade head" in cloud_command
|
||||
assert "device-cloud-api --host 0.0.0.0" in cloud_command
|
||||
|
||||
|
||||
def test_example_environment_contains_only_placeholder_credentials() -> None:
|
||||
def test_example_environment_contains_no_static_credentials() -> None:
|
||||
values = {}
|
||||
for line in (ROOT / ".env.example").read_text(encoding="utf-8").splitlines():
|
||||
if line and not line.startswith("#"):
|
||||
name, value = line.split("=", 1)
|
||||
values[name] = value
|
||||
|
||||
public_credentials = json.loads(values["CLOUD_PUBLIC_CREDENTIALS_JSON"])
|
||||
host_credentials = json.loads(values["CLOUD_HOST_CREDENTIALS_JSON"])
|
||||
enrollment_credentials = json.loads(values["CLOUD_ENROLLMENT_TOKENS_JSON"])
|
||||
|
||||
assert public_credentials[0]["token"].startswith("change-me-")
|
||||
assert host_credentials[0]["token"] == values["HOST_AGENT_TOKEN"]
|
||||
assert host_credentials[0]["token"].startswith("change-me-")
|
||||
assert host_credentials[0]["host_id"] == values["HOST_AGENT_HOST_ID"]
|
||||
assert enrollment_credentials[0]["token"].startswith("change-me-")
|
||||
assert values["HOST_AGENT_IDENTITY_PATH"] == "/app/tasks/host_identity.json"
|
||||
assert set(values) == {
|
||||
"IMAGE_TAG",
|
||||
"POSTGRES_DB",
|
||||
"POSTGRES_USER",
|
||||
"POSTGRES_PASSWORD",
|
||||
"CLOUD_API_PORT",
|
||||
}
|
||||
assert not set(values) & REMOVED_CREDENTIAL_SETTINGS
|
||||
|
||||
Reference in New Issue
Block a user