Bind Agent observations, review, enrichment and telemetry to current Zone
and inventory authority. Preserve immutable MAC ownership and historical
snapshots with stale-result rejection and transactional security audits.
Retire unbound direct/range/scheduled discovery and one/bulk legacy summary
writes in both modes. Require reconciled exact inventory identity or
explicit Zone-scoped IP selection for CSV metadata imports.
Independent Blocker #1 application-security re-audit: PASS on tree
cf863aadc9. Evidence: 141 focused PostgreSQL/HTTP
tests passed; Chromium and PowerShell checks passed. Two documented baseline
source assertions remain unchanged. Production deployment is not approved.
345 lines
15 KiB
Python
345 lines
15 KiB
Python
"""Focused model, service, router, and architecture tests for Agent Zones v1."""
|
|
|
|
import unittest
|
|
from pathlib import Path
|
|
from uuid import UUID, uuid4
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.agent_commands.dependencies import require_agent_identity
|
|
from app.agent_zones.dependencies import get_agent_zone_service
|
|
from app.agent_zones.exceptions import AgentZoneValidationError
|
|
from app.agent_zones.models import AgentCheckinRequest, AgentZoneCreateRequest
|
|
from app.agent_zones.router import router
|
|
from app.agent_zones.service import AgentZoneService
|
|
from app.identity import get_current_user, required_api_permission
|
|
|
|
|
|
SITE_A = UUID("00000000-0000-4000-8000-000000001601")
|
|
SITE_B = UUID("00000000-0000-4000-8000-000000001602")
|
|
ZONE_A = UUID("00000000-0000-4000-8000-000000001611")
|
|
ZONE_B = UUID("00000000-0000-4000-8000-000000001612")
|
|
MINER_A = UUID("00000000-0000-4000-8000-000000001621")
|
|
APP_ROOT = Path(__file__).parents[1] / "app"
|
|
|
|
|
|
def zone(zone_id=ZONE_A, site_id=SITE_A, *, enabled=True, name="Main Room"):
|
|
return {
|
|
"id": zone_id,
|
|
"site_id": site_id,
|
|
"name": name,
|
|
"description": None,
|
|
"is_enabled": enabled,
|
|
"assigned_agent_id": None,
|
|
"networks": [],
|
|
}
|
|
|
|
|
|
class FakeRepository:
|
|
def __init__(self):
|
|
self.zones = {ZONE_A: zone()}
|
|
self.agent = None
|
|
self.saved = []
|
|
self.assign_result = {MINER_A: "assigned"}
|
|
self.discovery_match = None
|
|
|
|
def get_agent(self, agent_id):
|
|
return self.agent if self.agent and self.agent["agent_id"] == agent_id else None
|
|
|
|
def get_zone(self, zone_id):
|
|
return self.zones.get(zone_id)
|
|
|
|
def list_usable_zones(self, site_id):
|
|
return [
|
|
value for value in self.zones.values()
|
|
if value["site_id"] == site_id and value["is_enabled"]
|
|
]
|
|
|
|
def list_zones(self, site_id=None):
|
|
return [
|
|
value for value in self.zones.values()
|
|
if site_id is None or value["site_id"] == site_id
|
|
]
|
|
|
|
def create_zone(self, *, site_id, name, description, networks):
|
|
created_id = uuid4()
|
|
self.zones[created_id] = {
|
|
**zone(created_id, site_id, name=name),
|
|
"description": description,
|
|
"networks": [
|
|
{"id": uuid4(), "network": network} for network in networks
|
|
],
|
|
}
|
|
return created_id
|
|
|
|
def save_checkin(self, **kwargs):
|
|
self.saved.append(kwargs)
|
|
self.agent = {
|
|
"agent_id": kwargs["agent_id"],
|
|
"site_id": kwargs["site_id"],
|
|
"agent_zone_id": kwargs["agent_zone_id"],
|
|
"site_name": kwargs["site_name"],
|
|
}
|
|
|
|
def get_agent_assignment(self, agent_id):
|
|
if not self.agent or self.agent["agent_id"] != agent_id:
|
|
return None
|
|
zone_row = self.zones.get(self.agent.get("agent_zone_id"))
|
|
return {
|
|
**self.agent,
|
|
"zone_enabled": zone_row["is_enabled"] if zone_row else None,
|
|
}
|
|
|
|
def get_agent_configuration(self, agent_id):
|
|
assignment = self.get_agent_assignment(agent_id)
|
|
if not assignment:
|
|
return None
|
|
zone_row = self.zones.get(assignment.get("agent_zone_id"))
|
|
return {
|
|
"agent_id": agent_id,
|
|
"site_id": assignment.get("site_id"),
|
|
"agent_zone_id": assignment.get("agent_zone_id"),
|
|
"site_name": assignment.get("site_name"),
|
|
"agent_zone_name": zone_row["name"] if zone_row else None,
|
|
"agent_zone_enabled": zone_row["is_enabled"] if zone_row else None,
|
|
"networks": [
|
|
item["network"] for item in (zone_row or {}).get("networks", [])
|
|
],
|
|
}
|
|
|
|
def find_miner_in_zone(self, *, agent_id, agent_zone_id, mac_address, ip_address):
|
|
self.discovery_match_request = {'agent_id':agent_id,'agent_zone_id':agent_zone_id,
|
|
'mac_address':mac_address,'ip_address':ip_address}
|
|
return self.discovery_match
|
|
|
|
def assign_miners_to_zone(self, miner_ids, agent_zone_id):
|
|
self.assigned = (list(miner_ids), agent_zone_id)
|
|
return {miner_id: self.assign_result.get(miner_id, "miner_not_found")
|
|
for miner_id in miner_ids}
|
|
|
|
|
|
class AgentZoneModelTests(unittest.TestCase):
|
|
def test_names_and_networks_are_normalized(self):
|
|
request = AgentZoneCreateRequest(
|
|
site_id=SITE_A,
|
|
name=" Main Room ",
|
|
description=" Local VLAN ",
|
|
networks=["10.30.0.0/24", "2001:db8::/64"],
|
|
)
|
|
self.assertEqual(request.name, "Main Room")
|
|
self.assertEqual(request.description, "Local VLAN")
|
|
self.assertEqual(request.networks, ["10.30.0.0/24", "2001:db8::/64"])
|
|
|
|
def test_invalid_host_network_duplicate_and_extra_fields_are_rejected(self):
|
|
for networks in (["10.30.0.7/24"], ["10.30.0.0/24", "10.30.0.0/24"]):
|
|
with self.subTest(networks=networks), self.assertRaises(Exception):
|
|
AgentZoneCreateRequest(site_id=SITE_A, name="Zone", networks=networks)
|
|
with self.assertRaises(Exception):
|
|
AgentZoneCreateRequest(site_id=SITE_A, name="Zone", unknown=True)
|
|
|
|
|
|
class AgentZoneServiceTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.repository = FakeRepository()
|
|
self.repository.agent = {"agent_id":"agent-a","site_id":SITE_A,"agent_zone_id":ZONE_A,"site_name":"North"}
|
|
self.service = AgentZoneService(self.repository)
|
|
|
|
def checkin(self, **kwargs):
|
|
return self.service.checkin(
|
|
"agent-a",
|
|
AgentCheckinRequest(agent_id="agent-a", site_name="North", **kwargs),
|
|
)
|
|
|
|
def test_established_assignment_and_same_assignment_are_allowed(self):
|
|
first = self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
|
|
second = self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
|
|
self.assertTrue(first.routing_available)
|
|
self.assertEqual(second.agent_zone_id, ZONE_A)
|
|
|
|
def test_explicit_zone_site_mismatch_and_identity_spoof_are_rejected(self):
|
|
with self.assertRaisesRegex(AgentZoneValidationError, "cannot change Sites"):
|
|
self.checkin(site_id=SITE_B, agent_zone_id=ZONE_A)
|
|
with self.assertRaisesRegex(AgentZoneValidationError, "authenticated"):
|
|
self.service.checkin(
|
|
"agent-a", AgentCheckinRequest(agent_id="agent-b", site_id=SITE_A)
|
|
)
|
|
|
|
def test_established_agent_cannot_change_zone_within_site(self):
|
|
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
|
|
self.repository.zones[ZONE_B] = zone(ZONE_B, SITE_A, name="Room 2")
|
|
with self.assertRaisesRegex(AgentZoneValidationError, "different Agent Zone"):
|
|
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_B)
|
|
self.assertEqual(self.repository.agent["agent_zone_id"], ZONE_A)
|
|
|
|
def test_established_agent_site_conflict_precedes_zone_conflict(self):
|
|
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
|
|
self.repository.zones[ZONE_B] = zone(ZONE_B, SITE_B, name="Remote")
|
|
with self.assertRaisesRegex(AgentZoneValidationError, "cannot change Sites"):
|
|
self.checkin(site_id=SITE_B, agent_zone_id=ZONE_B)
|
|
self.assertEqual(self.repository.agent["agent_zone_id"], ZONE_A)
|
|
|
|
def test_omitted_zone_preserves_established_assignment_and_site(self):
|
|
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
|
|
result = self.service.checkin(
|
|
"agent-a", AgentCheckinRequest(agent_id="agent-a", site_name="North")
|
|
)
|
|
self.assertEqual(result.site_id, SITE_A)
|
|
self.assertEqual(result.agent_zone_id, ZONE_A)
|
|
|
|
def test_transitional_resolution_requires_exactly_one_enabled_zone(self):
|
|
for zones in [{}, {ZONE_A: zone()}, {ZONE_A:zone(),ZONE_B:zone(ZONE_B,SITE_A)}]:
|
|
self.repository.zones=zones
|
|
self.repository.agent=None
|
|
with self.assertRaisesRegex(AgentZoneValidationError,"administrator-established"):
|
|
self.checkin(site_id=SITE_A)
|
|
|
|
def test_disabled_zone_is_not_selected_and_existing_assignment_is_factual(self):
|
|
self.repository.zones[ZONE_A]["is_enabled"]=False
|
|
existing=self.service.checkin("agent-a",AgentCheckinRequest(agent_id="agent-a"))
|
|
self.assertEqual(existing.routing_reason,"agent_zone_disabled")
|
|
self.assertEqual(existing.agent_zone_id,ZONE_A)
|
|
self.repository.agent=None
|
|
with self.assertRaisesRegex(AgentZoneValidationError,"administrator-established"):
|
|
self.checkin(site_id=SITE_A)
|
|
|
|
def test_payload_is_sanitized_before_persistence(self):
|
|
request = AgentCheckinRequest.model_validate({
|
|
"agent_id": "agent-a", "site_id": str(SITE_A),
|
|
"api3_password": "secret", "nested": {"access_token": "secret"},
|
|
"control_available": True,
|
|
})
|
|
self.service.checkin("agent-a", request)
|
|
payload = self.repository.saved[-1]["payload"]
|
|
self.assertNotIn("password", str(payload).lower())
|
|
self.assertNotIn("token", str(payload).lower())
|
|
self.assertTrue(payload["control_available"])
|
|
|
|
def test_miner_zone_assignment_surfaces_conflicts_without_rehoming(self):
|
|
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
|
|
self.repository.assign_result[MINER_A] = "agent_zone_conflict"
|
|
result = self.service.associate_miners_from_agent("agent-a", [MINER_A])
|
|
self.assertEqual(result.outcomes[MINER_A], "agent_zone_conflict")
|
|
self.assertEqual(self.repository.assigned, ([MINER_A], ZONE_A))
|
|
|
|
def test_routing_context_rejects_stale_recorded_zone(self):
|
|
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
|
|
result = self.service.routing_context("agent-a", ZONE_B)
|
|
self.assertEqual(result.routing_reason, "agent_zone_conflict")
|
|
self.assertIsNone(result.site_id)
|
|
|
|
def test_configuration_uses_only_authenticated_agent_zone_networks(self):
|
|
self.repository.zones[ZONE_A]["networks"] = [
|
|
{"id": uuid4(), "network": "10.30.0.0/24"}
|
|
]
|
|
self.repository.zones[ZONE_B] = zone(ZONE_B, SITE_B, name="Remote")
|
|
self.repository.zones[ZONE_B]["networks"] = [
|
|
{"id": uuid4(), "network": "10.80.0.0/24"}
|
|
]
|
|
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
|
|
|
|
configuration = self.service.configuration("agent-a")
|
|
|
|
self.assertEqual(configuration["agent_zone_id"], ZONE_A)
|
|
self.assertEqual(
|
|
[item["cidr"] for item in configuration["ip_ranges"]],
|
|
["10.30.0.0/24"],
|
|
)
|
|
self.assertNotIn("10.80.0.0/24", str(configuration))
|
|
|
|
|
|
def test_discovery_match_uses_only_authenticated_agent_zone(self):
|
|
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
|
|
self.repository.discovery_match = {"id": MINER_A, "matched_by": "mac"}
|
|
|
|
result = self.service.match_discovered_miner(
|
|
"agent-a",
|
|
mac_address="02:00:00:00:16:21",
|
|
ip_address="192.0.2.21",
|
|
)
|
|
|
|
self.assertEqual(result.miner_id, MINER_A)
|
|
self.assertEqual(result.matched_by, "mac")
|
|
self.assertEqual(
|
|
self.repository.discovery_match_request["agent_zone_id"], ZONE_A
|
|
)
|
|
|
|
def test_discovery_match_rejects_stale_recorded_zone_without_querying(self):
|
|
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
|
|
|
|
result = self.service.match_discovered_miner(
|
|
"agent-a",
|
|
recorded_zone_id=ZONE_B,
|
|
mac_address="02:00:00:00:16:21",
|
|
ip_address="192.0.2.21",
|
|
)
|
|
|
|
self.assertIsNone(result.miner_id)
|
|
self.assertEqual(result.routing_reason, "agent_zone_conflict")
|
|
self.assertFalse(hasattr(self.repository, "discovery_match_request"))
|
|
|
|
|
|
class AgentZoneRouterTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.repository = FakeRepository()
|
|
self.repository.agent = {"agent_id":"agent-a","site_id":SITE_A,"agent_zone_id":ZONE_A,"site_name":"North"}
|
|
self.service = AgentZoneService(self.repository)
|
|
self.app = FastAPI()
|
|
self.app.include_router(router)
|
|
self.app.dependency_overrides[get_agent_zone_service] = lambda: self.service
|
|
self.app.dependency_overrides[require_agent_identity] = lambda: "agent-a"
|
|
self.client = TestClient(self.app)
|
|
|
|
def tearDown(self):
|
|
self.client.close()
|
|
|
|
def authenticate(self, permissions):
|
|
self.app.dependency_overrides[get_current_user] = lambda: {
|
|
"id": str(uuid4()), "username": "operator", "role": "Custom",
|
|
"permissions": list(permissions),
|
|
}
|
|
|
|
def test_list_and_create_use_existing_agent_permissions(self):
|
|
self.assertEqual(self.client.get("/api/agent-zones").status_code, 401)
|
|
self.authenticate(("agents.view",))
|
|
self.assertEqual(self.client.get("/api/agent-zones").status_code, 200)
|
|
self.assertEqual(
|
|
self.client.post(
|
|
"/api/agent-zones", json={"site_id": str(SITE_A), "name": "Room 2"}
|
|
).status_code,
|
|
403,
|
|
)
|
|
self.authenticate(("agents.manage",))
|
|
response = self.client.post(
|
|
"/api/agent-zones",
|
|
json={"site_id": str(SITE_A), "name": "Room 2", "networks": ["10.40.0.0/24"]},
|
|
)
|
|
self.assertEqual(response.status_code, 201)
|
|
self.assertEqual(response.json()["data"]["name"], "Room 2")
|
|
|
|
def test_checkin_uses_authenticated_agent_identity(self):
|
|
response = self.client.post(
|
|
"/api/agent/checkin",
|
|
json={"agent_id": "agent-a", "site_id": str(SITE_A),
|
|
"agent_zone_id": str(ZONE_A)},
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response.json()["agent_id"], "agent-a")
|
|
self.assertNotIn("secret", response.text.lower())
|
|
|
|
def test_permission_and_architecture_audits(self):
|
|
self.assertEqual(required_api_permission("GET", "/api/agent-zones"), "agents.view")
|
|
self.assertEqual(required_api_permission("POST", "/api/agent-zones"), "agents.manage")
|
|
router_source = (APP_ROOT / "agent_zones" / "router.py").read_text()
|
|
service_source = (APP_ROOT / "agent_zones" / "service.py").read_text()
|
|
repository_source = (APP_ROOT / "agent_zones" / "repository.py").read_text()
|
|
self.assertNotIn("psycopg", router_source)
|
|
self.assertNotIn("SELECT ", router_source)
|
|
self.assertNotIn("fastapi", service_source.lower())
|
|
self.assertNotIn("fastapi", repository_source.lower())
|
|
self.assertNotIn('@app.post("/api/agent/checkin")', (APP_ROOT / "main.py").read_text())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|