Skip to content

Commit ab0613e

Browse files
Added API models to update NTP settings, create NTP timeservers, and delete NTP timeservers. Added API endpoints for /api/v1/services/ntpd and /api/v1/services/ntpd/time_server
1 parent 5166401 commit ab0613e

7 files changed

Lines changed: 464 additions & 0 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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/APIEndpoint.inc");
17+
18+
class APIServicesNTPd extends APIEndpoint {
19+
public function __construct() {
20+
$this->url = "/api/v1/services/ntpd";
21+
}
22+
23+
protected function get() {
24+
return (new APIServicesNTPdRead())->call();
25+
}
26+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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/APIEndpoint.inc");
17+
18+
class APIServicesNTPdTimeServer extends APIEndpoint {
19+
public function __construct() {
20+
$this->url = "/api/v1/services/ntpd/time_server";
21+
}
22+
23+
protected function post() {
24+
return (new APIServicesNTPdTimeServerCreate())->call();
25+
}
26+
27+
protected function delete() {
28+
return (new APIServicesNTPdTimeServerDelete())->call();
29+
}
30+
}

pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,42 @@ function get($id, $data=[], $all=false) {
520520
"return" => $id,
521521
"message" => "DHCPd static mapping ID does not exist"
522522
],
523+
2046 => [
524+
"status" => "bad request",
525+
"code" => 400,
526+
"return" => $id,
527+
"message" => "NTPd orphan value must be between 1 and 15"
528+
],
529+
2047 => [
530+
"status" => "bad request",
531+
"code" => 400,
532+
"return" => $id,
533+
"message" => "Unknown NTPd interface specified"
534+
],
535+
2048 => [
536+
"status" => "bad request",
537+
"code" => 400,
538+
"return" => $id,
539+
"message" => "NTPd timeserver value is required"
540+
],
541+
2049 => [
542+
"status" => "bad request",
543+
"code" => 400,
544+
"return" => $id,
545+
"message" => "NTPd timeserver must be valid IP address or FQDN"
546+
],
547+
2050 => [
548+
"status" => "bad request",
549+
"code" => 400,
550+
"return" => $id,
551+
"message" => "Maximum limit of 10 NTPd timeservers has been met"
552+
],
553+
2051 => [
554+
"status" => "bad request",
555+
"code" => 400,
556+
"return" => $id,
557+
"message" => "NTPd timeserver does not exist"
558+
],
523559

524560
// 3000-3999 reserved for /interfaces API calls
525561
3000 => [
@@ -1744,6 +1780,7 @@ function get($id, $data=[], $all=false) {
17441780
"return" => $id,
17451781
"message" => "Authentication server name already in use"
17461782
],
1783+
17471784
//6000-6999 reserved for /routing API calls
17481785
6000 => [
17491786
"status" => "bad request",
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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 APIServicesNTPdRead extends APIModel {
21+
# Create our method constructor
22+
public function __construct() {
23+
parent::__construct();
24+
$this->privileges = ["page-all", "page-services-ntpd"];
25+
}
26+
27+
public function action() {
28+
# Return the current NTP configuration if it exists, otherwise return empty array
29+
if (isset($this->config["ntpd"])) {
30+
return APIResponse\get(0, $this->config["ntpd"]);
31+
} else {
32+
return APIResponse\get(0, []);
33+
}
34+
}
35+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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 APIServicesNTPdTimeServerCreate extends APIModel {
21+
public $timeservers;
22+
23+
# Create our method constructor
24+
public function __construct() {
25+
parent::__construct();
26+
$this->privileges = ["page-all", "page-services-ntpd"];
27+
$this->change_note = "Added NTP timeserver via API";
28+
$this->validated_data = $this->config["ntpd"];
29+
$this->timeservers = explode(" ", $this->config["system"]["timeservers"]);
30+
}
31+
32+
public function action() {
33+
# Save our updated configuration. Note this updates configuration in two different places.
34+
$this->config["ntpd"] = $this->validated_data;
35+
$this->config["system"]["timeservers"] = implode(" ", $this->timeservers);
36+
$this->write_config();
37+
38+
# Reconfigure NTP to apply the changes. Since values are stored in two places, simply return the initial data.
39+
system_ntp_configure();
40+
return APIResponse\get(0, $this->initial_data);
41+
}
42+
43+
public function validate_payload() {
44+
$this->__validate_timeserver_limit();
45+
$this->__validate_timeserver();
46+
$this->__validate_ispool();
47+
$this->__validate_noselect();
48+
$this->__validate_prefer();
49+
}
50+
51+
private function __validate_timeserver_limit() {
52+
# Before adding a new timeserver, ensure we are not already at our maximum limit
53+
if (count($this->timeservers) > 10) {
54+
$this->errors[] = APIResponse\get(2050);
55+
}
56+
}
57+
58+
private function __validate_timeserver() {
59+
# Check for our required 'timeserver' payload value
60+
# Timeservers must be tracked separately as they are stored under system > timeservers rather than the
61+
# NTP service settings. Do not use the validated_data property to track valid timeserver input.
62+
if (isset($this->initial_data["timeserver"])) {
63+
# Ensure the value is a valid IP address or hostname
64+
if (is_ipaddr($this->initial_data["timeserver"]) or is_fqdn($this->initial_data["timeserver"])) {
65+
$this->timeservers[] = $this->initial_data['timeserver'];
66+
} else {
67+
$this->errors[] = APIResponse\get(2049);
68+
}
69+
} else {
70+
$this->errors[] = APIResponse\get(2048);
71+
}
72+
}
73+
74+
private function __validate_ispool() {
75+
# Check for our optional 'ispool' payload value
76+
if ($this->initial_data['ispool'] === true) {
77+
$this->validated_data["ispool"] = $this->validated_data["ispool"].$this->initial_data["timeserver"]." ";
78+
}
79+
}
80+
81+
private function __validate_noselect() {
82+
# Check for our optional 'noselect' payload value
83+
if ($this->initial_data['noselect'] === true) {
84+
$this->validated_data["noselect"] = $this->validated_data["noselect"].$this->initial_data["timeserver"]." ";
85+
}
86+
}
87+
88+
private function __validate_prefer() {
89+
# Check for our optional 'prefer' payload value
90+
if ($this->initial_data['prefer'] === true) {
91+
$this->validated_data["prefer"] = $this->validated_data["prefer"].$this->initial_data["timeserver"]." ";
92+
}
93+
}
94+
95+
96+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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 APIServicesNTPdTimeServerDelete extends APIModel {
21+
public $timeservers;
22+
23+
# Create our method constructor
24+
public function __construct() {
25+
parent::__construct();
26+
$this->privileges = ["page-all", "page-services-ntpd"];
27+
$this->change_note = "Deleted NTP timeserver via API";
28+
$this->validated_data = $this->config["ntpd"];
29+
$this->timeservers = explode(" ", $this->config["system"]["timeservers"]);
30+
}
31+
32+
public function action() {
33+
# Save our updated configuration. Note this updates configuration in two different places.
34+
$this->config["ntpd"] = $this->validated_data;
35+
$this->config["system"]["timeservers"] = implode(" ", $this->timeservers);
36+
$this->write_config();
37+
38+
# Reconfigure NTP to apply the changes. Since values are stored in two places, simply return the initial data.
39+
system_ntp_configure();
40+
return APIResponse\get(0, $this->initial_data);
41+
}
42+
43+
public function validate_payload() {
44+
$this->__validate_timeserver();
45+
}
46+
47+
private function __validate_timeserver() {
48+
# Before deleting the timeserver, ensure it exists
49+
if (in_array($this->initial_data["timeserver"], $this->timeservers)) {
50+
# Remove the timeserver from our system timeservers
51+
unset($this->timeservers[array_search($this->initial_data["timeserver"], $this->timeservers)]);
52+
53+
# Remove the timeserver from the ntpd ispool
54+
$ispool_arr = explode(" ", $this->validated_data["ispool"]);
55+
if (in_array($this->initial_data["timeserver"], $ispool_arr)) {
56+
unset($ispool_arr[array_search($this->initial_data["timeserver"], $ispool_arr)]);
57+
$this->validated_data["ispool"] = implode(" ", $ispool_arr);
58+
}
59+
60+
# Remove the timeserver from the ntpd noselect
61+
$noselect_arr = explode(" ", $this->validated_data["noselect"]);
62+
if (in_array($this->initial_data["timeserver"], $noselect_arr)) {
63+
unset($noselect_arr[array_search($this->initial_data["timeserver"], $noselect_arr)]);
64+
$this->validated_data["noselect"] = implode(" ", $noselect_arr);
65+
}
66+
67+
# Remove the timeserver from the ntpd prefer
68+
$prefer_arr = explode(" ", $this->validated_data["prefer"]);
69+
if (in_array($this->initial_data["timeserver"], $prefer_arr)) {
70+
unset($prefer_arr[array_search($this->initial_data["timeserver"], $prefer_arr)]);
71+
$this->validated_data["prefer"] = implode(" ", $prefer_arr);
72+
}
73+
}
74+
# Otherwise, return error if the request name server does not exist
75+
else {
76+
$this->errors[] = APIResponse\get(2051);
77+
}
78+
}
79+
80+
}

0 commit comments

Comments
 (0)