Skip to content

Commit 2c444a8

Browse files
Added the sched field to firewall rule creations and updates to apply firewall schedules, updated unit tests and documentation accordingly
1 parent 5b7349d commit 2c444a8

5 files changed

Lines changed: 124 additions & 2 deletions

File tree

docs/documentation.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,7 +2209,7 @@
22092209
}
22102210
},
22112211
"url": {
2212-
"raw": "https://{{$hostname}}/api/v1/firewall/rule?type=string&interface=string&ipprotocol=string&protocol=string&icmptype=string or array&src=string&dst=string&srcport=string or integer&dstport=string or integer&gateway=string&disabled=boolean&descr=string&log=boolean&top=boolean&apply=boolean",
2212+
"raw": "https://{{$hostname}}/api/v1/firewall/rule?type=string&interface=string&ipprotocol=string&protocol=string&icmptype=string or array&src=string&dst=string&srcport=string or integer&dstport=string or integer&gateway=string&sched=string&disabled=boolean&descr=string&log=boolean&top=boolean&apply=boolean",
22132213
"protocol": "https",
22142214
"host": [
22152215
"{{$hostname}}"
@@ -2271,6 +2271,11 @@
22712271
"value": "string",
22722272
"description": "Set the routing gateway traffic will take upon match (optional)"
22732273
},
2274+
{
2275+
"key": "sched",
2276+
"value": "string",
2277+
"description": "Set a firewall schedule to apply to this rule. This must be an existing firewall schedule name. (optional)"
2278+
},
22742279
{
22752280
"key": "disabled",
22762281
"value": "boolean",
@@ -2317,7 +2322,7 @@
23172322
}
23182323
},
23192324
"url": {
2320-
"raw": "https://{{$hostname}}/api/v1/firewall/rule?tracker=string or Integer&type=string&interface=string&ipprotocol=string&protocol=string&icmptype=string or array&src=string&dst=string&srcport=string or integer&dstport=string or integer&gateway=string&disabled=boolean&descr=string&log=boolean&top=boolean&apply=boolean",
2325+
"raw": "https://{{$hostname}}/api/v1/firewall/rule?tracker=string or Integer&type=string&interface=string&ipprotocol=string&protocol=string&icmptype=string or array&src=string&dst=string&srcport=string or integer&dstport=string or integer&gateway=string&disabled=boolean&descr=string&log=boolean&top=boolean&apply=boolean&sched=string",
23212326
"protocol": "https",
23222327
"host": [
23232328
"{{$hostname}}"
@@ -2408,6 +2413,11 @@
24082413
"key": "apply",
24092414
"value": "boolean",
24102415
"description": "Specify whether or not you would like this rule update to be applied immediately, or simply written to the configuration to be applied later. Typically, if you are updating multiple rules at once it Is best to set this to false and apply the changes afterwards using the `/api/v1/firewall/apply` endpoint. Otherwise, If you are only updating a single rule, you may set this true to apply it immediately. Defaults to false. (optional)"
2416+
},
2417+
{
2418+
"key": "sched",
2419+
"value": "string",
2420+
"description": "Update the firewall schedule to apply to this rule. This must be an existing firewall schedule name. Provide an empty string to remove the configured schedule from the rule. (optional)"
24112421
}
24122422
]
24132423
},

pfSense-pkg-API/files/etc/inc/api/models/APIFirewallRuleCreate.inc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,24 @@ class APIFirewallRuleCreate extends APIModel {
205205
}
206206
}
207207

208+
private function __validate_sched() {
209+
# Check for our optional 'sched' payload value
210+
if (isset($this->initial_data['sched'])) {
211+
# Loop through each configured schedule and check if it exists
212+
foreach ($this->config["schedules"]["schedule"] as $schedule) {
213+
# Check if the names match
214+
if ($this->initial_data["sched"] === $schedule["name"]) {
215+
$this->validated_data["sched"] = $this->initial_data["sched"];
216+
}
217+
}
218+
219+
# Set error if no match was found
220+
if (!isset($this->validated_data["sched"])) {
221+
$this->errors[] = APIResponse\get(4150);
222+
}
223+
}
224+
}
225+
208226
private function __validate_disabled() {
209227
# Check for our optional 'disabled' payload value
210228
if ($this->initial_data['disabled'] === true) {
@@ -244,6 +262,7 @@ class APIFirewallRuleCreate extends APIModel {
244262
$this->__validate_srcport();
245263
$this->__validate_dstport();
246264
$this->__validate_gateway();
265+
$this->__validate_sched();
247266
$this->__validate_disabled();
248267
$this->__validate_descr();
249268
$this->__validate_log();

pfSense-pkg-API/files/etc/inc/api/models/APIFirewallRuleUpdate.inc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,31 @@ class APIFirewallRuleUpdate extends APIModel {
228228
}
229229
}
230230

231+
private function __validate_sched() {
232+
# Check for our optional 'sched' payload value
233+
if (isset($this->initial_data['sched'])) {
234+
# Only try to set the schedule if the value is not empty
235+
if (!empty($this->initial_data["sched"])) {
236+
# Loop through each configured schedule and check if it exists
237+
foreach ($this->config["schedules"]["schedule"] as $schedule) {
238+
# Check if the names match
239+
if ($this->initial_data["sched"] === $schedule["name"]) {
240+
$this->validated_data["sched"] = $this->initial_data["sched"];
241+
}
242+
}
243+
244+
# Set error if no match was found
245+
if (!isset($this->validated_data["sched"])) {
246+
$this->errors[] = APIResponse\get(4150);
247+
}
248+
}
249+
# Unset the schedule from the rule if an empty value was provided
250+
else {
251+
unset($this->validated_data["sched"]);
252+
}
253+
}
254+
}
255+
231256
private function __validate_disabled() {
232257
# Check for our optional 'disabled' payload value
233258
if ($this->initial_data['disabled'] === true) {
@@ -272,6 +297,7 @@ class APIFirewallRuleUpdate extends APIModel {
272297
$this->__validate_srcport();
273298
$this->__validate_dstport();
274299
$this->__validate_gateway();
300+
$this->__validate_sched();
275301
$this->__validate_disabled();
276302
$this->__validate_descr();
277303
$this->__validate_log();

tests/test_api_v1_firewall_rule.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,22 @@ class APIUnitTestFirewallRule(unit_test_framework.APIUnitTest):
239239
"gateway": "INVALID"
240240
}
241241
},
242+
{
243+
"name": "Test schedule validation",
244+
"status": 400,
245+
"return": 4150,
246+
"payload": {
247+
"type": "pass",
248+
"interface": "wan",
249+
"ipprotocol": "inet",
250+
"protocol": "tcp",
251+
"src": "any",
252+
"dst": "any",
253+
"srcport": "any",
254+
"dstport": "any",
255+
"sched": "INVALID"
256+
}
257+
},
242258
]
243259
put_tests = [
244260
{
@@ -385,6 +401,22 @@ class APIUnitTestFirewallRule(unit_test_framework.APIUnitTest):
385401
"gateway": "INVALID"
386402
}
387403
},
404+
{
405+
"name": "Test schedule validation",
406+
"status": 400,
407+
"return": 4150,
408+
"payload": {
409+
"type": "pass",
410+
"interface": "wan",
411+
"ipprotocol": "inet",
412+
"protocol": "tcp",
413+
"src": "any",
414+
"dst": "any",
415+
"srcport": "any",
416+
"dstport": "any",
417+
"sched": "INVALID"
418+
}
419+
},
388420
]
389421
delete_tests = [
390422
{"name": "Delete firewall rule", "payload": {}}, # Tracker ID gets populated by post_post() method

tests/test_api_v1_firewall_schedule.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,34 @@ class APIUnitTestFirewallSchedule(unit_test_framework.APIUnitTest):
9191
"timerange": []
9292
}
9393
},
94+
{
95+
"name": "Create firewall rule using created schedule",
96+
"uri": "/api/v1/firewall/rule",
97+
"payload": {
98+
"interface": "wan",
99+
"ipprotocol": "inet",
100+
"protocol": "any",
101+
"type": "pass",
102+
"src": "any",
103+
"dst": "any",
104+
"sched": "Test_Schedule"
105+
}
106+
}
94107
]
95108
delete_tests = [
109+
{
110+
"name": "Check deleting schedule in use",
111+
"status": 400,
112+
"return": 4166,
113+
"payload": {
114+
"name": "Test_Schedule"
115+
}
116+
},
117+
{
118+
"name": "Delete firewall rule using schedule",
119+
"uri": "/api/v1/firewall/rule",
120+
"payload": {}
121+
},
96122
{
97123
"name": "Delete firewall schedule",
98124
"payload": {
@@ -115,4 +141,13 @@ class APIUnitTestFirewallSchedule(unit_test_framework.APIUnitTest):
115141
# TODO: Add test to check inability to delete schedules while they're in use
116142
]
117143

144+
def post_post(self):
145+
# Only proceed if we have a valid post response
146+
if self.post_responses:
147+
# Check if the last request was a successful firewall rule creation
148+
if self.post_responses[-1] and type(self.post_responses[-1]["data"]) == dict:
149+
# Add the tracker to delete payload that deletes the firewall rule using the schedule
150+
if self.post_responses[-1]["data"].get("tracker", None):
151+
self.delete_tests[1]["payload"]["tracker"] = self.post_responses[-1]["data"]["tracker"]
152+
118153
APIUnitTestFirewallSchedule()

0 commit comments

Comments
 (0)