-from typing import List, Optional
+from typing import List, Optional, Union
from knot_resolver_manager.datamodel.network_schema import AddressRenumberingSchema
from knot_resolver_manager.datamodel.types import (
nodata: bool = False
+class ForwardServerSchema(SchemaNode):
+ pass
+
+
class PolicySchema(SchemaNode):
"""
Configuration of policy rule.
message: Deny message for 'deny' action.
reroute: Configuration for 'reroute' action.
answer: Answer definition for 'answer' action.
- mirror: Mirroring parameters for 'mirror' action.
+ servers: Servers configuration for 'mirror', 'forward', 'forward-tls' and 'stub' action.
"""
action: PolicyActionEnum
message: Optional[str] = None
reroute: Optional[List[AddressRenumberingSchema]] = None
answer: Optional[AnswerSchema] = None
- mirror: Optional[List[IPAddressOptionalPort]] = None
+ servers: Optional[Union[List[IPAddressOptionalPort], List[ForwardServerSchema]]] = None
def _validate(self) -> None:
+ servers = ["mirror", "forward", "forward-tls", "stub"]
+
+ def _field(action: str) -> str:
+ if action in servers:
+ return "servers"
+ return {"deny": "message"}.get(action, action)
+
+ configurable_actions = ["deny", "reroute", "answer"] + servers
+
# checking for missing mandatory fields for actions
- mandatory_fields = ["reroute", "answer", "mirror"]
- if self.action in mandatory_fields and not getattr(self, self.action):
- raise ValueError(f"missing mandatory field '{self.action}' for '{self.action}' action")
+ field = _field(self.action)
+ if self.action in configurable_actions and not getattr(self, field):
+ raise ValueError(f"missing mandatory field '{field}' for '{self.action}' action")
# checking for unnecessary fields
- for action in ["deny"] + mandatory_fields:
- field = {"deny": "message"}.get(action, action)
- if getattr(self, field) and not self.action == action:
- raise ValueError(f"'{field}' field can only be defined for '{self.action}' action")
+ for action in configurable_actions + ["deny"]:
+ field = _field(action)
+ if getattr(self, field) and _field(self.action) != field:
+ raise ValueError(f"'{field}' field can only be defined for '{action}' action")
from pytest import raises
from knot_resolver_manager.datamodel.policy_schema import PolicySchema
+from knot_resolver_manager.datamodel.types import PolicyActionEnum
from knot_resolver_manager.exceptions import KresManagerException
+from knot_resolver_manager.utils.types import get_generic_type_arguments
+
+noconfig_actions = [
+ "pass",
+ "drop",
+ "refuse",
+ "tc",
+ "debug-always",
+ "debug-cache-miss",
+ "qtrace",
+ "reqtrace",
+]
+configurable_actions = ["deny", "reroute", "answer", "mirror", "forward", "forward-tls", "stub"]
+policy_actions = get_generic_type_arguments(PolicyActionEnum)
+
+
+@pytest.mark.parametrize("val", [item for item in policy_actions if item not in configurable_actions])
+def test_policy_action_valid(val: Any):
+ PolicySchema({"action": val})
@pytest.mark.parametrize("val", [{"action": "invalid-action"}])
-def test_simple_actions_invalid(val: Dict[str, Any]):
+def test_action_invalid(val: Dict[str, Any]):
with raises(KresManagerException):
- PolicySchema({"action": "invalid-action"})
+ PolicySchema(val)
@pytest.mark.parametrize(
"val",
[
- "pass",
- "drop",
- "refuse",
- "tc",
- "debug-always",
- "debug-cache-miss",
- "qtrace",
- "reqtrace",
+ {"action": "deny", "message": "this is deny message"},
+ {
+ "action": "reroute",
+ "reroute": [
+ {"source": "192.0.2.0/24", "destination": "127.0.0.0"},
+ {"source": "10.10.10.0/24", "destination": "192.168.1.0"},
+ ],
+ },
+ {"action": "answer", "answer": {"rtype": "AAAA", "rdata": "192.0.2.7"}},
+ {"action": "mirror", "servers": ["192.0.2.1@5353", "2001:148f:ffff::1"]},
+ {"action": "forward", "servers": ["192.0.2.1@5353", "2001:148f:ffff::1"]},
+ {"action": "forward-tls", "servers": ["192.0.2.1@5353", "2001:148f:ffff::1"]},
+ {"action": "stub", "servers": ["192.0.2.1@5353", "2001:148f:ffff::1"]},
],
)
-def test_message_invalid(val: str):
- with raises(KresManagerException):
- PolicySchema({"action": f"{val}", "message": "this is deny message"})
+def test_policy_valid(val: Dict[str, Any]):
+ PolicySchema(val)
@pytest.mark.parametrize(
{"action": "mirror"},
{"action": "pass", "reroute": [{"source": "192.0.2.0/24", "destination": "127.0.0.0"}]},
{"action": "pass", "answer": {"rtype": "AAAA", "rdata": "::1"}},
- {"action": "pass", "mirror": ["127.0.0.1@5353"]},
+ {"action": "pass", "servers": ["127.0.0.1@5353"]},
],
)
-def test_invalid(val: Dict[str, Any]):
+def test_policy_invalid(val: Dict[str, Any]):
with raises(KresManagerException):
PolicySchema(val)
+
+
+@pytest.mark.parametrize(
+ "val",
+ noconfig_actions,
+)
+def test_policy_message_invalid(val: str):
+ with raises(KresManagerException):
+ PolicySchema({"action": f"{val}", "message": "this is deny message"})