fix issue where you couldn't update ports
Build and Push Container / build-and-push (push) Successful in 26s

This commit is contained in:
2026-08-01 19:56:39 -04:00
parent e360546de9
commit 11389edf72
4 changed files with 587 additions and 9 deletions
+286 -1
View File
@@ -9,6 +9,7 @@ from main import (
RuleConfig,
build_zone_map,
load_config,
policy_matches_config,
process_rule,
send_ntfy_notification,
)
@@ -128,7 +129,7 @@ class TestProcessRule:
assert change["rule_name"] == "Test Rule"
assert change["error"] is None
def test_skips_rule_with_matching_ip(self) -> None:
def test_skips_rule_with_matching_config(self) -> None:
mock_session = MagicMock()
existing_policy = {
"id": "pol-1",
@@ -143,6 +144,14 @@ class TestProcessRule:
},
},
},
"destination": {"zoneId": "lan-id", "trafficFilter": None},
"action": {"type": "ALLOW", "allowReturnTraffic": True},
"ipProtocolScope": {"ipVersion": "IPV4", "protocolFilter": None},
"enabled": True,
"loggingEnabled": False,
"index": 0,
"metadata": {},
"description": None,
}
mock_session.get.return_value = MagicMock(json=MagicMock(return_value={"data": [existing_policy]}), raise_for_status=MagicMock())
@@ -224,3 +233,279 @@ class TestProcessRule:
assert change["action"] == "failed"
assert "Failed to list policies" in (change["error"] or "")
def test_updates_rule_with_different_ports_same_ip(self) -> None:
mock_session = MagicMock()
existing_policy = {
"id": "pol-1",
"name": "Test Rule",
"source": {
"zoneId": "wan-id",
"trafficFilter": {
"type": "IP_ADDRESS",
"ipAddressFilter": {
"type": "IP_ADDRESSES",
"items": [{"type": "IP_ADDRESS", "value": "1.2.3.4"}],
},
},
},
"destination": {
"zoneId": "lan-id",
"trafficFilter": {
"type": "PORT",
"portFilter": {
"type": "PORTS",
"items": [
{"type": "PORT_NUMBER", "value": 80},
{"type": "PORT_NUMBER", "value": 443},
],
},
},
},
"action": {"type": "ALLOW", "allowReturnTraffic": True},
"ipProtocolScope": {"ipVersion": "IPV4", "protocolFilter": None},
"enabled": True,
"loggingEnabled": False,
"index": 0,
"metadata": {},
"description": None,
}
mock_session.get.return_value = MagicMock(json=MagicMock(return_value={"data": [existing_policy]}), raise_for_status=MagicMock())
mock_session.put.return_value = MagicMock(json=MagicMock(return_value={"id": "pol-1"}), raise_for_status=MagicMock())
rule: RuleConfig = {
"name": "Test Rule",
"source_zone": "WAN",
"dest_zone": "LAN",
"ip_version": "IPV4",
"action": "ALLOW",
"dest_ports": [80, 443, 8080],
}
zone_map = {"WAN": "wan-id", "LAN": "lan-id"}
change = process_rule(mock_session, "https://unifi.local", "site-1", rule, zone_map, "1.2.3.4")
assert change["action"] == "updated"
mock_session.put.assert_called_once()
class TestPolicyMatchesConfig:
def test_returns_true_when_all_match(self) -> None:
existing_policy = {
"id": "pol-1",
"name": "Test Rule",
"source": {
"zoneId": "wan-id",
"trafficFilter": {
"type": "IP_ADDRESS",
"ipAddressFilter": {
"type": "IP_ADDRESSES",
"items": [{"type": "IP_ADDRESS", "value": "1.2.3.4"}],
},
},
},
"destination": {
"zoneId": "lan-id",
"trafficFilter": {
"type": "PORT",
"portFilter": {
"type": "PORTS",
"items": [
{"type": "PORT_NUMBER", "value": 80},
{"type": "PORT_NUMBER", "value": 443},
],
},
},
},
"action": {"type": "ALLOW", "allowReturnTraffic": True},
"ipProtocolScope": {
"ipVersion": "IPV4",
"protocolFilter": {
"type": "NAMED_PROTOCOL",
"protocol": {"name": "TCP"},
},
},
"enabled": True,
"loggingEnabled": False,
"index": 0,
"metadata": {},
"description": "Test description",
}
rule: RuleConfig = {
"name": "Test Rule",
"source_zone": "WAN",
"dest_zone": "LAN",
"ip_version": "IPV4",
"action": "ALLOW",
"allow_return_traffic": True,
"protocol": "tcp",
"dest_ports": [443, 80],
"logging_enabled": False,
"enabled": True,
"description": "Test description",
}
zone_map = {"WAN": "wan-id", "LAN": "lan-id"}
result = policy_matches_config(existing_policy, rule, zone_map, "1.2.3.4") # type: ignore[arg-type]
assert result is True
def test_returns_false_different_ports(self) -> None:
existing_policy = {
"id": "pol-1",
"name": "Test Rule",
"source": {
"zoneId": "wan-id",
"trafficFilter": {
"type": "IP_ADDRESS",
"ipAddressFilter": {
"type": "IP_ADDRESSES",
"items": [{"type": "IP_ADDRESS", "value": "1.2.3.4"}],
},
},
},
"destination": {
"zoneId": "lan-id",
"trafficFilter": {
"type": "PORT",
"portFilter": {
"type": "PORTS",
"items": [
{"type": "PORT_NUMBER", "value": 80},
{"type": "PORT_NUMBER", "value": 443},
],
},
},
},
"action": {"type": "ALLOW", "allowReturnTraffic": True},
"ipProtocolScope": {"ipVersion": "IPV4", "protocolFilter": None},
"enabled": True,
"loggingEnabled": False,
"index": 0,
"metadata": {},
"description": None,
}
rule: RuleConfig = {
"name": "Test Rule",
"source_zone": "WAN",
"dest_zone": "LAN",
"ip_version": "IPV4",
"action": "ALLOW",
"dest_ports": [80, 443, 8080],
}
zone_map = {"WAN": "wan-id", "LAN": "lan-id"}
result = policy_matches_config(existing_policy, rule, zone_map, "1.2.3.4") # type: ignore[arg-type]
assert result is False
def test_returns_false_different_protocol(self) -> None:
existing_policy = {
"id": "pol-1",
"name": "Test Rule",
"source": {
"zoneId": "wan-id",
"trafficFilter": {
"type": "IP_ADDRESS",
"ipAddressFilter": {
"type": "IP_ADDRESSES",
"items": [{"type": "IP_ADDRESS", "value": "1.2.3.4"}],
},
},
},
"destination": {"zoneId": "lan-id", "trafficFilter": None},
"action": {"type": "ALLOW", "allowReturnTraffic": True},
"ipProtocolScope": {
"ipVersion": "IPV4",
"protocolFilter": {
"type": "NAMED_PROTOCOL",
"protocol": {"name": "TCP"},
},
},
"enabled": True,
"loggingEnabled": False,
"index": 0,
"metadata": {},
"description": None,
}
rule: RuleConfig = {
"name": "Test Rule",
"source_zone": "WAN",
"dest_zone": "LAN",
"ip_version": "IPV4",
"action": "ALLOW",
"protocol": "UDP",
}
zone_map = {"WAN": "wan-id", "LAN": "lan-id"}
result = policy_matches_config(existing_policy, rule, zone_map, "1.2.3.4") # type: ignore[arg-type]
assert result is False
def test_returns_false_different_action(self) -> None:
existing_policy = {
"id": "pol-1",
"name": "Test Rule",
"source": {
"zoneId": "wan-id",
"trafficFilter": {
"type": "IP_ADDRESS",
"ipAddressFilter": {
"type": "IP_ADDRESSES",
"items": [{"type": "IP_ADDRESS", "value": "1.2.3.4"}],
},
},
},
"destination": {"zoneId": "lan-id", "trafficFilter": None},
"action": {"type": "ALLOW", "allowReturnTraffic": True},
"ipProtocolScope": {"ipVersion": "IPV4", "protocolFilter": None},
"enabled": True,
"loggingEnabled": False,
"index": 0,
"metadata": {},
"description": None,
}
rule: RuleConfig = {
"name": "Test Rule",
"source_zone": "WAN",
"dest_zone": "LAN",
"ip_version": "IPV4",
"action": "BLOCK",
}
zone_map = {"WAN": "wan-id", "LAN": "lan-id"}
result = policy_matches_config(existing_policy, rule, zone_map, "1.2.3.4") # type: ignore[arg-type]
assert result is False
def test_returns_false_different_enabled(self) -> None:
existing_policy = {
"id": "pol-1",
"name": "Test Rule",
"source": {
"zoneId": "wan-id",
"trafficFilter": {
"type": "IP_ADDRESS",
"ipAddressFilter": {
"type": "IP_ADDRESSES",
"items": [{"type": "IP_ADDRESS", "value": "1.2.3.4"}],
},
},
},
"destination": {"zoneId": "lan-id", "trafficFilter": None},
"action": {"type": "ALLOW", "allowReturnTraffic": True},
"ipProtocolScope": {"ipVersion": "IPV4", "protocolFilter": None},
"enabled": False,
"loggingEnabled": False,
"index": 0,
"metadata": {},
"description": None,
}
rule: RuleConfig = {
"name": "Test Rule",
"source_zone": "WAN",
"dest_zone": "LAN",
"ip_version": "IPV4",
"action": "ALLOW",
"enabled": True,
}
zone_map = {"WAN": "wan-id", "LAN": "lan-id"}
result = policy_matches_config(existing_policy, rule, zone_map, "1.2.3.4") # type: ignore[arg-type]
assert result is False
+126
View File
@@ -9,7 +9,10 @@ from unifi_firewall import (
FirewallPolicyCreatePayload,
build_policy_payload,
create_policy,
get_action_from_policy,
get_dest_ports_from_policy,
get_policy,
get_protocol_from_policy,
get_session,
get_source_ip_from_policy,
list_policies,
@@ -297,3 +300,126 @@ class TestGetSourceIpFromPolicy:
ip = get_source_ip_from_policy(policy) # type: ignore[arg-type]
assert ip is None
class TestGetDestPortsFromPolicy:
def test_extracts_ports_and_ranges(self) -> None:
policy: dict[str, Any] = {
"id": "pol-1",
"name": "Test",
"source": {"zoneId": "wan-zone", "trafficFilter": None},
"destination": {
"zoneId": "lan-zone",
"trafficFilter": {
"type": "PORT",
"portFilter": {
"type": "PORTS",
"matchOpposite": False,
"items": [
{"type": "PORT_NUMBER", "value": 80},
{"type": "PORT_NUMBER", "value": 443},
{"type": "PORT_NUMBER_RANGE", "start": 8000, "stop": 9000},
],
},
},
},
"action": {"type": "ALLOW", "allowReturnTraffic": True},
"ipProtocolScope": {"ipVersion": "IPV4"},
"enabled": True,
"loggingEnabled": False,
"index": 0,
"metadata": {},
}
result = get_dest_ports_from_policy(policy) # type: ignore[arg-type]
assert result is not None
dest_ports, dest_port_ranges = result
assert sorted(dest_ports) == [80, 443]
assert len(dest_port_ranges) == 1
assert dest_port_ranges[0] == {"start": 8000, "stop": 9000}
def test_returns_none_no_port_filter(self) -> None:
policy: dict[str, Any] = {
"destination": {
"zoneId": "lan-zone",
"trafficFilter": None,
},
}
result = get_dest_ports_from_policy(policy) # type: ignore[arg-type]
assert result is None
def test_returns_none_empty_items(self) -> None:
policy: dict[str, Any] = {
"destination": {
"zoneId": "lan-zone",
"trafficFilter": {
"type": "PORT",
"portFilter": {"type": "PORTS", "items": []},
},
},
}
result = get_dest_ports_from_policy(policy) # type: ignore[arg-type]
assert result is None
class TestGetProtocolFromPolicy:
def test_extracts_protocol(self) -> None:
policy: dict[str, Any] = {
"ipProtocolScope": {
"ipVersion": "IPV4",
"protocolFilter": {
"type": "NAMED_PROTOCOL",
"protocol": {"name": "TCP"},
},
},
}
protocol = get_protocol_from_policy(policy) # type: ignore[arg-type]
assert protocol == "TCP"
def test_returns_none_no_protocol_filter(self) -> None:
policy: dict[str, Any] = {
"ipProtocolScope": {
"ipVersion": "IPV4",
"protocolFilter": None,
},
}
protocol = get_protocol_from_policy(policy) # type: ignore[arg-type]
assert protocol is None
class TestGetActionFromPolicy:
def test_extracts_allow_action(self) -> None:
policy: dict[str, Any] = {
"action": {"type": "ALLOW", "allowReturnTraffic": True},
}
result = get_action_from_policy(policy) # type: ignore[arg-type]
assert result == ("ALLOW", True)
def test_extracts_allow_action_no_return_traffic(self) -> None:
policy: dict[str, Any] = {
"action": {"type": "ALLOW", "allowReturnTraffic": False},
}
result = get_action_from_policy(policy) # type: ignore[arg-type]
assert result == ("ALLOW", False)
def test_extracts_block_action(self) -> None:
policy: dict[str, Any] = {
"action": {"type": "BLOCK"},
}
result = get_action_from_policy(policy) # type: ignore[arg-type]
assert result == ("BLOCK", False)
def test_extracts_reject_action(self) -> None:
policy: dict[str, Any] = {
"action": {"type": "REJECT"},
}
result = get_action_from_policy(policy) # type: ignore[arg-type]
assert result == ("REJECT", False)