Skip to content

Commit 09dba92

Browse files
feat(PortForward): allow 'target' to use interface IP #721
1 parent f67eaed commit 09dba92

2 files changed

Lines changed: 69 additions & 3 deletions

File tree

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/PortForward.inc

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class PortForward extends Model {
2727
public PortField $source_port;
2828
public FilterAddressField $destination;
2929
public PortField $destination_port;
30-
public StringField $target;
30+
public FilterAddressField $target;
3131
public PortField $local_port;
3232
public BooleanField $disabled;
3333
public BooleanField $nordr;
@@ -96,9 +96,19 @@ class PortForward extends Model {
9696
conditions: ['protocol' => ['tcp', 'udp', 'tcp/udp']],
9797
help_text: 'The destination port this port forward rule applies to. Set to `null` to allow any destination port.',
9898
);
99-
$this->target = new StringField(
99+
$this->target = new FilterAddressField(
100100
required: true,
101-
validators: [new IPAddressValidator(allow_ipv4: true, allow_ipv6: true, allow_alias: true)],
101+
allow_ipaddr: true,
102+
allow_subnet: false,
103+
allow_alias: true,
104+
allow_interface: false,
105+
allow_interface_ip: true,
106+
allow_interface_groups: false,
107+
allow_any: false,
108+
allow_invert: false,
109+
allow_self: false,
110+
allow_l2tp: false,
111+
allow_pppoe: false,
102112
help_text: 'The IP address or alias of the internal host to forward matching traffic to.',
103113
);
104114
$this->local_port = new PortField(

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsPortForwardTestCase.inc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
namespace RESTAPI\Tests;
44

55
use RESTAPI\Core\TestCase;
6+
use RESTAPI\Models\FirewallAlias;
67
use RESTAPI\Models\FirewallRule;
78
use RESTAPI\Models\PortForward;
9+
use RESTAPI\Responses\ValidationError;
810

911
class APIModelsPortForwardTestCase extends TestCase {
1012
/**
@@ -227,4 +229,58 @@ class APIModelsPortForwardTestCase extends TestCase {
227229
$rule_q = FirewallRule::query(associated_rule_id: $port_forward->associated_rule_id->value);
228230
$this->assert_is_false($rule_q->exists());
229231
}
232+
233+
/**
234+
* Ensures the target field accepts IP addresses, aliases and interface IPs
235+
*/
236+
public function test_target_validation(): void {
237+
# Create an alias to test with
238+
$alias = new FirewallAlias(name: "testalias", type:"host");
239+
$alias->create();
240+
241+
# Set values we expect to be allowed vs disallowed
242+
$allowed_values = ["1.2.3.4", "wan:ip", "testalias"];
243+
$disallowed_values = ["example.com", "wan", "self", "l2tp", "1.2.3.4/24"];
244+
245+
# Check each allowed value and ensure it does not throw an exception during validation
246+
foreach ($allowed_values as $value) {
247+
$this->assert_does_not_throw(
248+
callable: function () use ($value) {
249+
$port_forward = new PortForward(
250+
data: [
251+
'interface' => 'wan',
252+
'protocol' => 'tcp',
253+
'source' => 'any',
254+
'destination' => 'wan:ip',
255+
'destination_port' => '8443',
256+
'target' => $value,
257+
'local_port' => '4443',
258+
],
259+
);
260+
$port_forward->validate();
261+
},
262+
);
263+
}
264+
265+
# Check each disallowed value and ensure it throws an exception during validation
266+
foreach ($disallowed_values as $value) {
267+
$this->assert_throws(
268+
exceptions: ["ValidationError"],
269+
callable: function () use ($value) {
270+
$port_forward = new PortForward(
271+
data: [
272+
'interface' => 'wan',
273+
'protocol' => 'tcp',
274+
'source' => 'any',
275+
'destination' => 'wan:ip',
276+
'destination_port' => '8443',
277+
'target' => $value,
278+
'local_port' => '4443',
279+
],
280+
);
281+
$port_forward->validate();
282+
}
283+
);
284+
}
285+
}
230286
}

0 commit comments

Comments
 (0)