Skip to content

Commit 4afe54f

Browse files
Merge pull request #88 from jaredhendrickson13/ui_upgrades
UI Upgrades & Features
2 parents f468aab + 6688a07 commit 4afe54f

7 files changed

Lines changed: 340 additions & 253 deletions

File tree

docs/documentation.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4356,15 +4356,15 @@
43564356
"header": [],
43574357
"body": {
43584358
"mode": "raw",
4359-
"raw": "{\n \"persist\": false, \n \"jwt_exp\": 86400, \n \"authmode\": \"token\",\n \"hashalgo\": \"sha512\", \n \"keybytes\": 64, \n \"allowed_interfaces\": [\"WAN\"]\n}",
4359+
"raw": "{\n \"persist\": false, \n \"jwt_exp\": 86400, \n \"authmode\": \"token\",\n \"hashalgo\": \"sha512\", \n \"keybytes\": 64, \n \"allowed_interfaces\": [\"WAN\"],\n \"custom_headers\": {\"custom-header-1\": \"Value1\", \"custom-header-2\": \"Value2\"},\n \"allow_options\": true\n}",
43604360
"options": {
43614361
"raw": {
43624362
"language": "json"
43634363
}
43644364
}
43654365
},
43664366
"url": {
4367-
"raw": "https://{{$hostname}}/api/v1/system/api?enable=boolean&persist=boolean&readonly=boolean&available_interfaces=array&authmode=string&jwt_exp=integer&keyhash=string&keybytes=integer",
4367+
"raw": "https://{{$hostname}}/api/v1/system/api?enable=boolean&persist=boolean&readonly=boolean&allow_options=boolean&available_interfaces=array&authmode=string&jwt_exp=integer&keyhash=string&keybytes=integer&custom_headers=array",
43684368
"protocol": "https",
43694369
"host": [
43704370
"{{$hostname}}"
@@ -4391,6 +4391,11 @@
43914391
"value": "boolean",
43924392
"description": "Enable read only mode. If set to `true`, the API will only answer read (GET) requests. This also means you will not be able to disable read only mode from the API. "
43934393
},
4394+
{
4395+
"key": "allow_options",
4396+
"value": "boolean",
4397+
"description": "Enable/disable the OPTIONS request method from API responses. If set to `true`, the API will answer OPTIONS requests. If set to `false`, the API will return a 405 Method Not Allowed response. (optional)"
4398+
},
43944399
{
43954400
"key": "available_interfaces",
43964401
"value": "array",
@@ -4415,6 +4420,11 @@
44154420
"key": "keybytes",
44164421
"value": "integer",
44174422
"description": "Update the key byte strength to use when generating API tokens. Choices are `16`, `32` and `64`. This Is only applicable when the `authmode` setting Is set to `token`. (optional)"
4423+
},
4424+
{
4425+
"key": "custom_headers",
4426+
"value": "array",
4427+
"description": "Update the custom response headers for the API to return in API responses. This must be an array of key-value pairs (e.g. `{\"custom-header\": \"custom-header-value}`. To revert custom headers to the default state, simply pass in an empty array. In most cases, custom headers are not necessary. An example use case for custom headers is setting CORS policy headers required by some frontend web applications. (optional)"
44184428
}
44194429
]
44204430
},

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

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,42 @@ class APIEndpoint {
5353

5454
# Listen for HTTP requests and call the corresponding method
5555
public function listen() {
56+
$pkg_config = APITools\get_api_config()[1];
57+
58+
# Before responding, ensure the request method is allowed
5659
if ($_SERVER["REQUEST_METHOD"] === "GET") {
5760
$resp = (new APIQuery($this->get(), $this->query_excludes))->query();
58-
} elseif ($_SERVER["REQUEST_METHOD"] === "POST") {
61+
}
62+
elseif ($_SERVER["REQUEST_METHOD"] === "POST") {
5963
$resp = $this->post();
60-
} elseif ($_SERVER["REQUEST_METHOD"] === "PUT") {
64+
}
65+
elseif ($_SERVER["REQUEST_METHOD"] === "PUT") {
6166
$resp = $this->put();
62-
} elseif ($_SERVER["REQUEST_METHOD"] === "DELETE") {
67+
}
68+
elseif ($_SERVER["REQUEST_METHOD"] === "DELETE") {
6369
$resp = $this->delete();
64-
} else {
70+
}
71+
# Only allow OPTIONS requests if the allow options setting is checked
72+
elseif ($_SERVER["REQUEST_METHOD"] === "OPTIONS" and isset($pkg_config["allow_options"])) {
73+
$resp = APIResponse\get(0);
74+
}
75+
# Method is not allowed when no match, set error response
76+
else {
6577
$resp = APIResponse\get(2);
6678
}
6779

68-
# Format the HTTP response as JSON and set response code
80+
# Add custom response headers if configured
81+
if (!empty($pkg_config["custom_headers"])) {
82+
foreach ($pkg_config["custom_headers"] as $name=>$value) {
83+
header(strval($name).": ".strval($value));
84+
}
85+
}
86+
87+
# Add API required response headers, these will override any custom headers
6988
header("Content-Type: application/json", true);
7089
header("Referer: no-referrer");
90+
91+
# Format the HTTP response as JSON and set response code
7192
http_response_code($resp["code"]);
7293
echo json_encode($resp) . PHP_EOL;
7394
session_destroy();

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,19 @@ function get($id, $data=[], $all=false) {
240240
"status" => "bad request",
241241
"code" => 400,
242242
"return" => $id,
243-
"message" => "Unsupport API token bytes count"
243+
"message" => "Unsupported API token bytes count"
244+
],
245+
1025 => [
246+
"status" => "bad request",
247+
"code" => 400,
248+
"return" => $id,
249+
"message" => "API custom headers must be an array containing key-value pairs"
250+
],
251+
1026 => [
252+
"status" => "bad request",
253+
"code" => 400,
254+
"return" => $id,
255+
"message" => "API custom header key-value pairs must be string types"
244256
],
245257

246258
// 2000-2999 reserved for /services API calls

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ class APISystemAPIUpdate extends APIModel {
5959
}
6060
}
6161

62+
private function __validate_allow_options() {
63+
# Check for our optional 'allow_options' payload value
64+
if ($this->initial_data['allow_options'] === true) {
65+
$this->validated_data["allow_options"] = "";
66+
} elseif ($this->initial_data['allow_options'] === false) {
67+
unset($this->validated_data["allow_options"]);
68+
}
69+
}
70+
6271
private function __validate_allowed_interfaces() {
6372
# Local variables
6473
$non_config_ifs = array("any" => "Any", "localhost" => "Link-local");
@@ -85,6 +94,28 @@ class APISystemAPIUpdate extends APIModel {
8594
}
8695
}
8796

97+
private function __validate_custom_headers() {
98+
# Check for our optional 'custom_headers' payload value
99+
if (isset($this->initial_data["custom_headers"]) and !empty($this->initial_data["custom_headers"])) {
100+
if (is_array($this->initial_data["custom_headers"])) {
101+
# Loop through each requested header and ensure types are valid
102+
foreach ($this->initial_data["custom_headers"] as $key=>$value) {
103+
if (!is_string($key) or !is_string($value)) {
104+
$this->errors[] = APIResponse\get(1026);
105+
break;
106+
}
107+
}
108+
$this->validated_data["custom_headers"] = $this->initial_data["custom_headers"];
109+
} else {
110+
$this->errors[] = APIResponse\get(1025);
111+
}
112+
}
113+
# When the custom_headers was passed in but is empty, unset custom headers
114+
elseif (isset($this->initial_data["custom_headers"]) and empty($this->initial_data["custom_headers"])) {
115+
unset($this->validated_data["custom_headers"]);
116+
}
117+
}
118+
88119
private function __validate_authmode() {
89120
# Check for our option 'authmode' payload value
90121
if (isset($this->initial_data["authmode"])) {
@@ -137,10 +168,12 @@ class APISystemAPIUpdate extends APIModel {
137168
$this->__validate_enable();
138169
$this->__validate_persist();
139170
$this->__validate_readonly();
171+
$this->__validate_allow_options();
140172
$this->__validate_allowed_interfaces();
141173
$this->__validate_authmode();
142174
$this->__validate_jwt_exp();
143175
$this->__validate_keyhash();
144176
$this->__validate_keybytes();
177+
$this->__validate_custom_headers();
145178
}
146179
}

0 commit comments

Comments
 (0)