Skip to content

Commit f74c73b

Browse files
Created APIFirewallScheduleUpdate model to update existing schedules, updated unit tests accordingly
1 parent 2c444a8 commit f74c73b

3 files changed

Lines changed: 177 additions & 0 deletions

File tree

pfSense-pkg-API/files/etc/inc/api/endpoints/APIFirewallSchedule.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ class APIFirewallSchedule extends APIEndpoint {
2828
return (new APIFirewallScheduleCreate())->call();
2929
}
3030

31+
protected function put() {
32+
return (new APIFirewallScheduleUpdate())->call();
33+
}
34+
3135
protected function delete() {
3236
return (new APIFirewallScheduleDelete())->call();
3337
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
// Copyright 2021 Jared Hendrickson
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
require_once("api/framework/APIModel.inc");
17+
require_once("api/framework/APIResponse.inc");
18+
19+
20+
class APIFirewallScheduleUpdate extends APIModel {
21+
# Create our method constructor
22+
public function __construct() {
23+
parent::__construct();
24+
$this->privileges = ["page-all", "page-firewall-schedules-edit"];
25+
$this->change_note = "Modified firewall schedule via API";
26+
}
27+
28+
public function action() {
29+
# Update this schedule in the config and reload the firewall filter
30+
$this->config["schedules"]["schedule"][$this->id] = $this->validated_data;
31+
$this->__sort_schedules();
32+
$this->write_config();
33+
filter_configure();
34+
35+
return APIResponse\get(0, $this->validated_data);
36+
}
37+
38+
public function validate_payload() {
39+
$this->__validate_name();
40+
$this->__validate_timerange();
41+
$this->__validate_descr();
42+
}
43+
44+
private function __validate_name() {
45+
# Check for our required `name` payload value
46+
if (isset($this->initial_data["name"])) {
47+
if ($this->get_schedule_id_by_name($this->initial_data["name"], true)) {
48+
$this->id = $this->get_schedule_id_by_name($this->initial_data["name"]);
49+
$this->validated_data = $this->config["schedules"]["schedule"][$this->id];
50+
} else {
51+
$this->errors[] = APIResponse\get(4150);
52+
}
53+
} else {
54+
$this->errors[] = APIResponse\get(4146);
55+
}
56+
}
57+
58+
private function __validate_timerange() {
59+
# Check for the optional `timerange` payload value
60+
if (isset($this->initial_data["timerange"])) {
61+
# Initialize the array to store validated time ranges
62+
$this->validated_data["timerange"];
63+
64+
# Require at least 1 time range to be configured
65+
if (is_array($this->initial_data["timerange"]) and count($this->initial_data["timerange"]) >= 1) {
66+
# Loop through each requested time range to validate
67+
foreach ($this->initial_data["timerange"] as $tr_data) {
68+
# Validate the time range using the APIFirewallScheduleTimeRangeCreate model
69+
$time_range = new APIFirewallScheduleTimeRangeCreate();
70+
$time_range->initial_data = $tr_data;
71+
$time_range->validate_payload(true);
72+
73+
# Add the validated time range if no errors were found
74+
if (empty($time_range->errors)) {
75+
$this->validated_data["timerange"][] = $time_range->validated_data;
76+
} else {
77+
$this->errors = $this->errors + $time_range->errors;
78+
}
79+
}
80+
} else {
81+
$this->errors[] = APIResponse\get(4162);
82+
}
83+
}
84+
}
85+
86+
private function __validate_descr() {
87+
# Check for the optional `descr` payload value
88+
if (isset($this->initial_data["descr"])) {
89+
$this->validated_data["descr"] = strval($this->initial_data["descr"]);
90+
}
91+
}
92+
93+
public function get_schedule_id_by_name($name, $as_bool=false) {
94+
# Loop through each schedule configured and check it's name
95+
foreach ($this->config["schedules"]["schedule"] as $id=>$schedule) {
96+
# Check if this $schedule's name matches our requested name
97+
if ($name === $schedule["name"]) {
98+
return ($as_bool) ? true : $id;
99+
}
100+
}
101+
return ($as_bool) ? false : null;
102+
}
103+
104+
private static function __compare_schedules($a, $b) {
105+
return strcmp($a['name'], $b['name']);
106+
}
107+
108+
private function __sort_schedules() {
109+
if (is_array($this->config['schedules']['schedule'])) {
110+
usort($this->config['schedules']['schedule'], ["APIFirewallScheduleCreate", "__compare_schedules"]);
111+
}
112+
}
113+
}

tests/test_api_v1_firewall_schedule.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
class APIUnitTestFirewallSchedule(unit_test_framework.APIUnitTest):
1818
uri = "/api/v1/firewall/schedule"
19+
get_tests = [
20+
{"name": "Read all firewall schedules"}
21+
]
1922
post_tests = [
2023
{
2124
"name": "Create firewall schedule",
@@ -105,6 +108,63 @@ class APIUnitTestFirewallSchedule(unit_test_framework.APIUnitTest):
105108
}
106109
}
107110
]
111+
put_tests = [
112+
{
113+
"name": "Update firewall schedule",
114+
"payload": {
115+
"name": "Test_Schedule",
116+
"descr": "Updated unit test",
117+
"timerange": [
118+
{
119+
"month": "1,3,5,12",
120+
"day": "10,20,25,25",
121+
"hour": "0:15-23:00",
122+
"rangedescr": "Updated unit test"
123+
},
124+
{
125+
"position": "1,3,5",
126+
"hour": "10:15-12:00",
127+
"rangedescr": "Updated unit test"
128+
}
129+
]
130+
}
131+
},
132+
{
133+
"name": "Check name requirement",
134+
"status": 400,
135+
"return": 4146
136+
},
137+
{
138+
"name": "Check updating non-existent firewall schedule",
139+
"status": 400,
140+
"return": 4150,
141+
"payload": {
142+
"name": "INVALID"
143+
}
144+
},
145+
{
146+
"name": "Check update with no changes",
147+
"payload": {
148+
"name": "Test_Schedule"
149+
}
150+
},
151+
{
152+
"name": "Check update with changed description only",
153+
"payload": {
154+
"name": "Test_Schedule",
155+
"descr": "Update unit test description only"
156+
}
157+
},
158+
{
159+
"name": "Check time range minimum length constraint",
160+
"status": 400,
161+
"return": 4162,
162+
"payload": {
163+
"name": "Test_Schedule",
164+
"timerange": []
165+
}
166+
},
167+
]
108168
delete_tests = [
109169
{
110170
"name": "Check deleting schedule in use",

0 commit comments

Comments
 (0)