Skip to content

Commit 86a3074

Browse files
Merge pull request #15 from jaredhendrickson13/v100_bug_fixes
v1.0.0 Bug Fixes
2 parents 82cf9f0 + 474d594 commit 86a3074

12 files changed

Lines changed: 183 additions & 126 deletions

File tree

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,29 @@ webConfigurator are required to make calls to the API endpoints
2121
# Installation
2222
To install pfSense API, simply run the following command from the pfSense shell:<br>
2323
```
24-
pkg add https://github.com/jaredhendrickson13/pfsense-api/releases/download/v1.0.0/pfSense-2-4-pkg-API-1.0_0.txz && /etc/rc.restart_webgui
24+
pkg add https://github.com/jaredhendrickson13/pfsense-api/releases/latest/download/pfSense-2.4-pkg-API.txz && /etc/rc.restart_webgui
2525
```
2626

27-
To uninstall, run the following command:<br>
27+
To uninstall pfSense API, run the following command:<br>
2828
```
2929
pkg delete pfSense-pkg-API
3030
```
3131

32+
To update pfSense API to latest version, run the following command:
33+
```
34+
pkg delete pfSense-pkg-API && pkg add https://github.com/jaredhendrickson13/pfsense-api/releases/latest/download/pfSense-2.4-pkg-API.txz && /etc/rc.restart_webgui
35+
```
36+
3237
### Notes:
38+
- pfSense API is supported on the pfSense 2.5 developer snapshots. To install the 2.5 package, simply change the `2.4` in the install URL to `2.5`.
3339
- In order for pfSense to apply some required web server changes, it is required to restart the webConfigurator after installing the package
3440
- If you do not have shell access to pfSense, you can still install via the webConfigurator by navigating to
3541
'Diagnostics > Command Prompt' and enter the commands there
42+
- When updating pfSense, **_you must reinstall pfSense API afterwards_**. Unfortunately, pfSense removes all existing packages and only reinstalls packages found within pfSense's package repositories. Since pfSense API is not an official package in pfSense's repositories, it does not get reinstalled automatically.
3643

3744

3845
# UI Settings & Documentation
39-
After installation, you will be able to access the API user interface pages within the pfSense webConfigurator. These will be found under System > API. The settings tab will allow you change various API settings such as enabled API interfaces, authentication modes, and more. Additionally, the documentation tab will give you access to an embedded documentation tool that makes it easy to view the full API documentation with context to your pfSense instance.
46+
After installation, you will be able to access the API user interface pages within the pfSense webConfigurator. These will be found under System > API. The settings tab will allow you change various API settings such as enabled API interfaces, authentication modes, and more. Additionally, the documentation tab will give you access to an embedded documentation tool that makes it easy to view the full API documentation in context to your pfSense instance.
4047

4148
### Notes:
4249
- Users must hold the `page-all` or `page-system-api` privileges to access the API page within the webConfigurator

docs/CONTRIBUTING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ writing a model that tests user's local database credentials and do not want the
100100
auth mode you would specify `$this->set_auth_mode = "local";` to always force local authentication. Defaults to the
101101
API's configured auth mode in the /api/ webConfigurator page.
102102

103+
- `$this->set_read_mode` : Allows the read-only API setting to be bypassed for this model. If you set this value to
104+
`true` the model will be allowed to use POST, PUT or DELETE methods even when the API is in read-only mode. There is
105+
rarely a use case for this. Do not override this property unless absolutely needed.
106+
103107
- `$this->change_note` : Sets the description of the action that occurred via API. This value will be shown in the
104108
change logs found at Diagnostics > Backup & Restore > Config History. This defaults to "Made unknown change via API".
105109
This is only necessary if your API model writes changes to the configuration.

docs/documentation.json

Lines changed: 84 additions & 84 deletions
Large diffs are not rendered by default.

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class APIAuth {
2020
private $api_config;
2121
private $request;
2222
public $auth_mode;
23+
public $read_mode;
2324
public $req_privs;
2425
public $username;
2526
public $privs;
@@ -28,9 +29,10 @@ class APIAuth {
2829
public $is_authorized;
2930

3031
# Create our method constructor
31-
public function __construct($req_privs, $enforce_auth_mode=null){
32+
public function __construct($req_privs, $enforce_auth_mode=null, $read_mode=null){
3233
$this->api_config = APITools\get_api_config()[1];
3334
$this->auth_mode = (is_null($enforce_auth_mode)) ? $this->api_config["authmode"] : $enforce_auth_mode;
35+
$this->read_mode = (is_null($read_mode)) ? array_key_exists("readonly", $this->api_config) : $read_mode;
3436
$this->request = APITools\get_request_data();
3537
$this->req_privs = $req_privs;
3638
$this->privs = [];
@@ -113,17 +115,16 @@ class APIAuth {
113115
# AUTHORIZATION #
114116
# Checks if the current user has the necessary privileges to access this resource
115117
public function authorize() {
116-
// Local variables
118+
# Local variables
117119
$authorized = false;
118120
$client_config =& getUserEntry($this->username);;
119121
$this->privs = get_user_privileges($client_config);
120-
$read_only = array_key_exists("readonly", $this->api_config);
121122

122123
# If no require privileges were given, assume call is always authorized
123124
if (!empty($this->req_privs)) {
124-
// Ensure our API is not read only OR if it is read only, only authorize the request if it is a GET request
125-
if ($read_only === false or ($read_only === true and $_SERVER['REQUEST_METHOD'] === "GET")) {
126-
// Loop through each of our req privs and ensure the client has them, also check if access is read only
125+
# If API is in readonly mode, only allow GET requests
126+
if (!$this->read_mode or ($this->read_mode and $_SERVER['REQUEST_METHOD'] === "GET")) {
127+
# Loop through each of our req privs and ensure the client has them, also check if access is read only
127128
foreach ($this->req_privs as &$priv) {
128129
if (in_array($priv, $this->privs)) {
129130
$authorized = true;

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,15 @@ class APIModel {
3030
public $change_note;
3131
public $requires_auth;
3232
public $set_auth_mode;
33+
public $bypass_read_mode;
3334

3435
public function __construct() {
3536
global $config;
3637
error_reporting(E_ERROR);
3738
$this->config =& $config;
3839
$this->privileges = ["page-all"];
39-
$this->client = null;
4040
$this->requires_auth = true;
41-
$this->set_auth_mode = null;
4241
$this->change_note = "Made unknown change via API";
43-
$this->id = null;
4442
$this->validate_id = true;
4543
$this->initial_data = APITools\get_request_data();
4644
$this->validated_data = [];
@@ -95,7 +93,7 @@ class APIModel {
9593
}
9694

9795
private function check_authentication() {
98-
$this->client = new APIAuth($this->privileges, $this->set_auth_mode);
96+
$this->client = new APIAuth($this->privileges, $this->set_auth_mode, $this->bypass_read_mode);
9997
if ($this->requires_auth === true) {
10098
if (!$this->client->is_authenticated) {
10199
$this->errors[] = APIResponse\get(3);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class APIAccessTokenCreate extends APIModel {
2121
public function __construct() {
2222
parent::__construct();
2323
$this->set_auth_mode = "local";
24+
$this->bypass_read_mode = false;
2425
}
2526

2627
# Validate our API configurations auth mode (must be JWT)

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ class APIFirewallRuleCreate extends APIModel {
3434
}
3535

3636
public function validate_payload() {
37+
# Include static rule ID
38+
$this->validated_data["id"] = "";
39+
3740
# Check for our required 'type' payload value
3841
if (isset($this->initial_data["type"])) {
3942
$type_options = array("pass", "block", "reject");
@@ -179,8 +182,8 @@ class APIFirewallRuleCreate extends APIModel {
179182

180183

181184
# Check for our optional 'disabled' payload value
182-
if ($this->initial_data['disabled'] !== true) {
183-
$this->validated_data["id"] = "";
185+
if ($this->initial_data['disabled'] === true) {
186+
$this->validated_data["disabled"] = "";
184187
}
185188

186189
# Check for our optional 'descr' payload value

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

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class APIFirewallRuleUpdate extends APIModel {
5151
$this->errors[] = APIResponse\get(4031);
5252
}
5353

54-
# Check for our required 'type' payload value
54+
# Check for our optional 'type' payload value
5555
if (isset($this->initial_data["type"])) {
5656
$type_options = array("pass", "block", "reject");
5757
# Ensure our type is a valid option
@@ -62,7 +62,7 @@ class APIFirewallRuleUpdate extends APIModel {
6262
}
6363
}
6464

65-
# Check for our required 'interface' payload value
65+
# Check for our optional 'interface' payload value
6666
if (isset($this->initial_data['interface'])) {
6767
$this->initial_data['interface'] = APITools\get_pfsense_if_id($this->initial_data['interface']);
6868
# Check that we found the request pfSense interface ID
@@ -73,7 +73,7 @@ class APIFirewallRuleUpdate extends APIModel {
7373
}
7474
}
7575

76-
# Check for our required 'ipprotocol' payload value
76+
# Check for our optional 'ipprotocol' payload value
7777
if (isset($this->initial_data['ipprotocol'])) {
7878
$ipprotocol_options = array("inet", "inet6", "inet46");
7979
# Check that our ipprotocol value is a support option
@@ -84,13 +84,16 @@ class APIFirewallRuleUpdate extends APIModel {
8484
}
8585
}
8686

87-
# Check for our required 'protocol' payload value
87+
# Check for our optional 'protocol' payload value
8888
if (isset($this->initial_data['protocol'])) {
89-
$port_required = (!in_array($this->validated_data["protocol"], ["tcp", "udp", "tcp/udp"])) ? true : false;
89+
$missing_port = (!in_array($this->validated_data["protocol"], ["tcp", "udp", "tcp/udp"])) ? true : false;
90+
$requires_port = (in_array($this->initial_data["protocol"], ["tcp", "udp", "tcp/udp"])) ? true : false;
91+
$this->unset_ports();
9092
$protocol_options = [
9193
"any", "tcp", "udp", "tcp/udp", "icmp", "esp", "ah",
9294
"gre", "ipv6", "igmp", "pim", "ospf", "carp", "pfsync"
9395
];
96+
9497
# Check that our protocol value is a support option
9598
if (in_array($this->initial_data["protocol"], $protocol_options)) {
9699
# Don't add a specific protocol if any
@@ -102,9 +105,10 @@ class APIFirewallRuleUpdate extends APIModel {
102105
} else {
103106
$this->errors[] = APIResponse\get(4042);
104107
}
105-
$protocol = $this->initial_data['protocol'];
106108
} else {
107-
$this->errors[] = APIResponse\get(4036);
109+
# If a new protocol was not selected don't validate ports
110+
$requires_port = false;
111+
$missing_port = false;
108112
}
109113

110114
# Check for our optional 'icmpsubtype' payload value when our protocol is set to ICMP
@@ -130,7 +134,7 @@ class APIFirewallRuleUpdate extends APIModel {
130134
}
131135
}
132136

133-
# Check for our required 'src' payload value
137+
# Check for our optional 'src' payload value
134138
if (isset($this->initial_data['src'])) {
135139
// Check if our source and destination values are valid
136140
$dir_check = APITools\is_valid_rule_addr($this->initial_data['src'], "source");
@@ -141,7 +145,7 @@ class APIFirewallRuleUpdate extends APIModel {
141145
}
142146
}
143147

144-
# Check for our required 'src' payload value
148+
# Check for our optional 'src' payload value
145149
if (isset($this->initial_data['dst'])) {
146150
// Check if our source and destination values are valid
147151
$dir_check = APITools\is_valid_rule_addr($this->initial_data['dst'], "destination");
@@ -152,16 +156,16 @@ class APIFirewallRuleUpdate extends APIModel {
152156
}
153157
}
154158

155-
# Check for our required 'srcport' and 'dstport' payload values if protocol is TCP, UDP or TCP/UDP
156-
if ($port_required && in_array($this->initial_data["protocol"], ["tcp", "udp", "tcp/udp"])) {
159+
# Check for our optional 'srcport' and 'dstport' payload values if protocol is TCP, UDP or TCP/UDP
160+
if ($requires_port) {
157161
if (isset($this->initial_data['srcport'])) {
158162
$val = str_replace("-", ":", $this->initial_data['srcport']);
159163
if (!is_port_or_range($val) and $val !== "any") {
160164
$this->errors[] = APIResponse\get(4048);
161165
} elseif ($val !== "any") {
162166
$this->validated_data["source"]["port"] = str_replace(":", "-", $val);;
163167
}
164-
} else {
168+
} elseif ($missing_port) {
165169
$this->errors[] = APIResponse\get(4047);
166170
}
167171

@@ -172,7 +176,7 @@ class APIFirewallRuleUpdate extends APIModel {
172176
} elseif ($val !== "any") {
173177
$this->validated_data["destination"]["port"] = str_replace(":", "-", $val);;
174178
}
175-
} else {
179+
} elseif ($missing_port) {
176180
$this->errors[] = APIResponse\get(4047);
177181
}
178182
}
@@ -187,10 +191,11 @@ class APIFirewallRuleUpdate extends APIModel {
187191
}
188192
}
189193

190-
191194
# Check for our optional 'disabled' payload value
192-
if ($this->initial_data['disabled'] !== true) {
193-
$this->validated_data["id"] = "";
195+
if ($this->initial_data['disabled'] === true) {
196+
$this->validated_data["disabled"] = "";
197+
} elseif ($this->initial_data['disabled'] === false) {
198+
unset($this->validated_data["disabled"]);
194199
}
195200

196201
# Check for our optional 'descr' payload value
@@ -214,4 +219,13 @@ class APIFirewallRuleUpdate extends APIModel {
214219
"username" => $this->client->username."@".$this->client->ip_address." (API)"
215220
];
216221
}
222+
223+
# Removes port specifications when updating to a protocol that is not port based
224+
private function unset_ports() {
225+
# If a new protocol was chosen that doesn't require a port, remove existing ports from the rule
226+
if ((!in_array($this->initial_data["protocol"], ["tcp", "udp", "tcp/udp"]))) {
227+
unset($this->validated_data["source"]["port"]);
228+
unset($this->validated_data["destination"]["port"]);
229+
}
230+
}
217231
}

0 commit comments

Comments
 (0)