Skip to content

Commit 93733a5

Browse files
author
Jared Hendrickson
committed
Converting alias endpoints from function based to object oriented
1 parent c138176 commit 93733a5

13 files changed

Lines changed: 744 additions & 34 deletions

File tree

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ If for any reason you need to access client data from within your API model clas
228228
property. This is an APIAuth object that contains details about the client:
229229

230230
- `$this->client->username` : Our client's corresponding pfSense username
231+
- `$this->client->ip_address` : The IP address of our client
231232
- `$this->client->is_authenticated` : Whether the client successfully authenticated. Keep in mind the APIBaseModel will
232233
handle all authentication and authorization for you, so this is likely unneeded.
233234
- `$this->client->is_authorized` : Whether the client was authorized. Keep in mind the APIBaseModel will
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
require_once("api/framework/APIBaseModel.inc");
3+
require_once("api/framework/APIResponse.inc");
4+
5+
6+
class APIFirewallAliases extends APIBaseModel {
7+
# Create our method constructor
8+
public function __construct() {
9+
parent::__construct();
10+
$this->methods = ["GET"];
11+
$this->privileges = ["page-all", "page-firewall-aliases"];
12+
}
13+
14+
public function action() {
15+
global $config;
16+
// Check that we have a configuration
17+
if (!empty($config["aliases"]["alias"])) {
18+
$alias_array = $config["aliases"]["alias"];
19+
} else {
20+
$alias_array = [];
21+
}
22+
return APIResponse\get(0, $alias_array);
23+
}
24+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
require_once("api/framework/APIBaseModel.inc");
3+
require_once("api/framework/APIResponse.inc");
4+
5+
class APIFirewallAliasesDelete extends APIBaseModel {
6+
# Create our method constructor
7+
public function __construct() {
8+
parent::__construct();
9+
$this->methods = ["POST"];
10+
$this->privileges = ["page-all", "page-firewall-aliases-edit"];
11+
}
12+
13+
public function action() {
14+
global $config;
15+
$_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging
16+
$change_note = " Deleted firewall alias via API"; // Add a change note
17+
$del_conf = $config["aliases"]["alias"][$this->validated_data["id"]]; // Capture alias config before deleting
18+
unset($config["aliases"]["alias"][$this->validated_data["id"]]); // Remove this alias from our configuration
19+
$config["aliases"]["alias"] = array_values($config["aliases"]["alias"]); // Reindex array
20+
write_config(sprintf(gettext($change_note))); // Apply our configuration change
21+
send_event("filter reload"); // Ensure our firewall filter is reloaded
22+
return APIResponse\get(0, $del_conf);
23+
}
24+
25+
public function validate_payload() {
26+
global $config;
27+
if (isset($this->initial_data['name'])) {
28+
$name = $this->initial_data['name'];
29+
$name = APITools\sanitize_str($name);
30+
31+
// Check that alias is not in use in our configuration
32+
if (!APITools\alias_in_use($name)) {
33+
// Loop through our current config and find the index ID for our alias to delete
34+
$c_count = 0; // Init loop counter
35+
foreach ($config["aliases"]["alias"] as $ce) {
36+
// Check if this entry matches our requested value
37+
if ($ce["name"] === $name) {
38+
$del_index = $c_count;
39+
break;
40+
}
41+
$c_count++;
42+
}
43+
44+
if (is_numeric($del_index)) {
45+
$this->validated_data["id"] = $del_index;
46+
} else {
47+
$this->errors[] = APIResponse\get(4055);
48+
}
49+
50+
} else {
51+
$this->errors[] = APIResponse\get(4051);
52+
}
53+
} else {
54+
$this->errors = APIResponse\get(4050);
55+
}
56+
57+
}
58+
}

pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ class APIFirewallNatPortForwardsAdd extends APIBaseModel {
2424

2525
// TODO: break this down into smaller field specific validators
2626
public function validate_payload() {
27-
global $config;
28-
$user_created_msg = $_SESSION["Username"]."@".$_SERVER["REMOTE_ADDR"]." (API)"; // Save the username and ip of client
27+
$user_created_msg = $this->client->username."@".$this->client->ip_address." (API)"; // Save the username and ip of client
2928
$allowed_nat_ref = ["enable", "disable", "purenat"]; // Save our allow NAT reflection types
3029
$allowed_prot = ["tcp", "udp", "tcp/udp", "icmp", "esp", "ah", "gre", "ipv6", "igmp", "pim", "ospf"];
3130
if (isset($this->initial_data['interface'])) {
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
<?php
2+
require_once("api/framework/APIBaseModel.inc");
3+
require_once("api/framework/APIResponse.inc");
4+
5+
class APIFirewallRulesAdd extends APIBaseModel {
6+
# Create our method constructor
7+
public function __construct() {
8+
parent::__construct();
9+
$this->methods = ["POST"];
10+
$this->privileges = ["page-all", "page-firewall-rules-edit"];
11+
}
12+
13+
public function action() {
14+
global $config;
15+
$next_rule_id = count($config["filter"]["rule"]); // Save our next rule ID
16+
$_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging
17+
$change_note = " Added firewall rule via API"; // Add a change note
18+
$config["filter"]["rule"][] = $this->validated_data; // Write to our master config
19+
APITools\sort_firewall_rules($this->initial_data["top"], $next_rule_id); // Sort our firewall rules
20+
write_config(sprintf(gettext($change_note))); // Apply our configuration change
21+
send_event("filter reload"); // Ensure our firewall filter is reloaded
22+
return APIResponse\get(0, $this->validated_data);
23+
}
24+
25+
// TODO: break this down into smaller field specific validators
26+
public function validate_payload() {
27+
global $config;
28+
$user_created_msg = $this->client->username."@".$this->client->ip_address." (API)";
29+
$allowed_rule_types = array("pass", "block", "reject"); // Array of allowed rule types
30+
$allowed_ip_prot = array("inet", "inet6", "inet46"); // Array of allowed IP protocols
31+
$allowed_prot = array(
32+
"any",
33+
"tcp",
34+
"udp",
35+
"tcp/udp",
36+
"icmp",
37+
"esp",
38+
"ah",
39+
"gre",
40+
"ipv6",
41+
"igmp",
42+
"pim",
43+
"ospf",
44+
"carp",
45+
"pfsync"
46+
);
47+
// Array of allowed ICMP subtypes
48+
$icmp_subtypes = array(
49+
"althost",
50+
"dataconv",
51+
"echorep",
52+
"echoreq",
53+
"inforep",
54+
"inforeq",
55+
"ipv6-here",
56+
"ipv6-where",
57+
"maskrep",
58+
"maskreq",
59+
"mobredir",
60+
"mobregrep",
61+
"mobregreq",
62+
"paramprob",
63+
"photuris",
64+
"redir",
65+
"routeradv",
66+
"routersol",
67+
"skip",
68+
"squench",
69+
"timerep",
70+
"timereq",
71+
"timex",
72+
"trace",
73+
"unreach"
74+
);
75+
if (isset($this->initial_data['type'])) {
76+
$type = $this->initial_data['type'];
77+
} else {
78+
$this->errors[] = APIResponse\get(4033);
79+
}
80+
if (isset($this->initial_data['interface'])) {
81+
$interface = $this->initial_data['interface'];
82+
$interface = APITools\get_pfsense_if_id($interface);
83+
} else {
84+
$this->errors[] = APIResponse\get(4034);
85+
86+
}
87+
if (isset($this->initial_data['ipprotocol'])) {
88+
$ipprotocol = $this->initial_data['ipprotocol'];
89+
} else {
90+
$this->errors[] = APIResponse\get(4035);
91+
}
92+
if (isset($this->initial_data['protocol'])) {
93+
$protocol = $this->initial_data['protocol'];
94+
} else {
95+
$this->errors[] = APIResponse\get(4036);
96+
}
97+
if (isset($this->initial_data['src'])) {
98+
$src = $this->initial_data['src'];
99+
} else {
100+
$this->errors[] = APIResponse\get(4037);
101+
}
102+
if (isset($this->initial_data['srcport'])) {
103+
$srcport = $this->initial_data['srcport'];
104+
}
105+
if (isset($this->initial_data['dst'])) {
106+
$dst = $this->initial_data['dst'];
107+
} else {
108+
$this->errors[] = APIResponse\get(4038);
109+
}
110+
if (isset($this->initial_data['dstport'])) {
111+
$dstport = $this->initial_data['dstport'];
112+
}
113+
if (isset($this->initial_data['icmptype'])) {
114+
$icmp_type = $this->initial_data['icmptype'];
115+
if (!is_array($icmp_type)) {
116+
$icmp_type = array($icmp_type);
117+
}
118+
}
119+
if (isset($this->initial_data['gateway'])) {
120+
$gateway = $this->initial_data['gateway'];
121+
}
122+
if (isset($this->initial_data['disabled'])) {
123+
$disabled = true;
124+
}
125+
if (isset($this->initial_data['descr'])) {
126+
$descr = $this->initial_data['descr'];
127+
}
128+
if (isset($this->initial_data['log'])) {
129+
$log = true;
130+
}
131+
if (isset($this->initial_data['top'])) {
132+
$this->initial_data['top'] = "top";
133+
}
134+
// INPUT VALIDATION/FORMATTING
135+
// Check that our required array/interface values are valid
136+
if (!in_array($type, $allowed_rule_types)) {
137+
$this->errors[] = APIResponse\get(4039);
138+
} elseif (!is_string($interface)) {
139+
$this->errors[] = APIResponse\get(4040);
140+
} elseif (!in_array($ipprotocol, $allowed_ip_prot)) {
141+
$this->errors[] = APIResponse\get(4041);
142+
} elseif (!in_array($protocol, $allowed_prot)) {
143+
$this->errors[] = APIResponse\get(4042);
144+
} elseif (isset($gateway) and !APITools\is_gateway($gateway)) {
145+
$this->errors[] = APIResponse\get(4043);
146+
}
147+
// Check if rule is not disabled
148+
if (!$disabled) {
149+
$this->validated_data["id"] = "";
150+
}
151+
// Check if logging is enabled
152+
if ($log) {
153+
$this->validated_data["log"] = "";
154+
}
155+
// Check if gateway was specified
156+
if (isset($gateway)) {
157+
$this->validated_data["gateway"] = $gateway;
158+
}
159+
$this->validated_data["type"] = $type;
160+
$this->validated_data["interface"] = $interface;
161+
$this->validated_data["ipprotocol"] = $ipprotocol;
162+
$this->validated_data["source"] = array();
163+
$this->validated_data["destination"] = array();
164+
$this->validated_data["descr"] = $descr;
165+
$this->validated_data["created"] = array("time" => time(), "username" => $user_created_msg);
166+
$this->validated_data["updated"] = $this->validated_data["created"];
167+
// Save our protocol if it is not 'any'
168+
if ($protocol !== "any") {
169+
$this->validated_data["protocol"] = $protocol;
170+
}
171+
// Add logging to config if enabled
172+
if ($log) {
173+
$this->validated_data["log"] = "";
174+
}
175+
// Check if our source and destination values are valid
176+
foreach (array("source" => $src, "destination" => $dst) as $dir => $val) {
177+
$dir_check = APITools\is_valid_rule_addr($val, $dir);
178+
if (!$dir_check["valid"] === true) {
179+
$input_err = true;
180+
if ($dir === "source") {
181+
$this->errors[] = APIResponse\get(4044);
182+
} else {
183+
$this->errors[] = APIResponse\get(4045);
184+
}
185+
} else {
186+
$this->validated_data = array_merge($this->validated_data, $dir_check["data"]);
187+
}
188+
}
189+
// Check if protocol calls for additional specifications
190+
if (in_array($protocol, array("tcp", "udp", "tcp/udp"))) {
191+
$port_req = true;
192+
} elseif ($protocol === "icmp") {
193+
// Check if user specified ICMP subtypes
194+
if (is_array($icmp_type)) {
195+
// Loop through each of our subtypes
196+
foreach ($icmp_type as $ict) {
197+
if (!in_array($ict, $icmp_subtypes)) {
198+
$this->errors[] = APIResponse\get(4046);
199+
}
200+
}
201+
// Write our ICMP subtype config
202+
$this->validated_data["icmptype"] = implode(",", $icmp_type);
203+
}
204+
}
205+
// Check our src and dst port values if ports are required
206+
if ($port_req) {
207+
if (!isset($srcport) or !isset($dstport)) {
208+
$this->errors[] = APIResponse\get(4047);
209+
210+
}
211+
foreach (array("source" => $srcport, "destination" => $dstport) as $dir => $val) {
212+
$val = str_replace("-", ":", $val);
213+
if (!is_port_or_range($val) and $val !== "any") {
214+
$input_err = true;
215+
if ($dir === "source") {
216+
$this->errors[] = APIResponse\get(4048);
217+
218+
} else {
219+
$this->errors[] = APIResponse\get(4049);
220+
}
221+
} elseif ($val !== "any") {
222+
$this->validated_data[$dir]["port"] = str_replace(":", "-", $val);;
223+
}
224+
}
225+
}
226+
}
227+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
require_once("api/framework/APIBaseModel.inc");
3+
require_once("api/framework/APIResponse.inc");
4+
5+
class APIFirewallRulesDelete extends APIBaseModel {
6+
# Create our method constructor
7+
public function __construct() {
8+
parent::__construct();
9+
$this->methods = ["POST"];
10+
$this->privileges = ["page-all", "page-firewall-rules-edit"];
11+
}
12+
13+
public function action() {
14+
global $config;
15+
$_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging
16+
$change_note = " Deleted firewall rule via API"; // Add a change note
17+
$del_rule = $config["filter"]["rule"][$this->validated_data["id"]]; // Save the rule we are deleting
18+
unset($config["filter"]["rule"][$this->validated_data["id"]]); // Remove rule from our config
19+
APITools\sort_firewall_rules(); // Sort our firewall rules
20+
write_config(sprintf(gettext($change_note))); // Apply our configuration change
21+
send_event("filter reload"); // Ensure our firewall filter is reloaded
22+
return APIResponse\get(0, $del_rule);
23+
}
24+
25+
public function validate_payload() {
26+
global $config;
27+
if (isset($this->initial_data['id'])) {
28+
// Check that our rule ID exists
29+
if (array_key_exists($this->initial_data["id"], $config["filter"]["rule"])) {
30+
$this->validated_data["id"] = $this->initial_data['id'];
31+
} else {
32+
$this->errors[] = APIResponse\get(4032);
33+
}
34+
} else {
35+
$this->errors[] = APIResponse\get(4031);
36+
}
37+
38+
}
39+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class APIAuth {
99
public $req_privs;
1010
public $username;
1111
public $privs;
12+
public $ip_address;
1213
public $is_authenticated;
1314
public $is_authorized;
1415

@@ -19,6 +20,7 @@ class APIAuth {
1920
$this->request = APITools\get_request_data();
2021
$this->req_privs = $req_privs;
2122
$this->privs = [];
23+
$this->ip_address = $_SERVER["REMOTE_ADDR"];
2224
$this->is_authenticated = $this->authenticate();
2325
$this->is_authorized = $this->authorize();
2426
}

0 commit comments

Comments
 (0)