Skip to content

Commit 510fb47

Browse files
Merge pull request #755 from jaredhendrickson13/next_patch
v2.6.2 Fixes
2 parents 19a0922 + 5af3236 commit 510fb47

8 files changed

Lines changed: 80 additions & 7 deletions

File tree

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
site_name: pfSense REST API Guide
2+
repo_url: https://github.com/jaredhendrickson13/pfsense-api
23
nav:
34
- General:
45
- Home: index.md

package-lock.json

Lines changed: 6 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"devDependencies": {
3+
"prettier": "^3.6.2",
34
"@prettier/plugin-php": "^0.24.0",
45
"@stoplight/spectral-cli": "^6.15.0"
56
}

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Fields/ForeignModelField.inc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ class ForeignModelField extends Field {
222222
$internal_value = $this->models[0]->{$this->model_field_internal}->_from_internal($internal_value);
223223
}
224224

225+
# If the model_field_internal, and the model_field are the same, return the internal value as-is.
226+
if ($this->model_field_internal === $this->model_field) {
227+
return $internal_value;
228+
}
229+
225230
# Query for the Model object this value relates to.
226231
$query_modelset = $this->__get_matches($this->model_field_internal, $internal_value);
227232

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use RESTAPI\Fields\IntegerField;
1010
use RESTAPI\Fields\InterfaceField;
1111
use RESTAPI\Fields\StringField;
1212
use RESTAPI\Fields\UIDField;
13+
use RESTAPI\Responses\ConflictError;
1314
use RESTAPI\Responses\ValidationError;
1415
use RESTAPI\Validators\IPAddressValidator;
1516
use RESTAPI\Validators\UniqueFromForeignModelValidator;
@@ -89,7 +90,6 @@ class VirtualIP extends Model {
8990
);
9091
$this->vhid = new IntegerField(
9192
required: true,
92-
unique: true,
9393
minimum: 1,
9494
maximum: 255,
9595
conditions: ['mode' => 'carp'],
@@ -178,6 +178,27 @@ class VirtualIP extends Model {
178178
return $subnet_bits;
179179
}
180180

181+
/**
182+
* Adds extra validation to the vhid field.
183+
* @param int $vhid The incoming `vhid` value to be validated.
184+
* @return int The validated `vhid` value to be set.
185+
* @throws ValidationError When the `vhid` value is already used by another CARP virtual IP on the same interface.
186+
*/
187+
public function validate_vhid(int $vhid): int {
188+
# Check for an existing CARP virtual IP with the same VHID on this interface
189+
$vip_q = $this->query(id__except: $this->id, mode: 'carp', interface: $this->interface->value, vhid: $vhid);
190+
191+
# Ensure no other CARP virtual IP on this interface is using the same VHID
192+
if ($vip_q->exists()) {
193+
$vip = $vip_q->first();
194+
throw new ConflictError(
195+
message: "Virtual IP with ID '$vip->id' is already using VHID '$vhid' on interface '{$this->interface->value}'",
196+
response_id: 'VIRTUALIP_VHID_ALREADY_IN_USE',
197+
);
198+
}
199+
return $vhid;
200+
}
201+
181202
/**
182203
* Obtains the current internal CARP status of this object
183204
* @return string|null Returns a string that indicates the current CARP status of this virtual IP, or null

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
namespace RESTAPI\Tests;
44

55
use RESTAPI\Core\TestCase;
6+
use RESTAPI\Core\TestCaseRetry;
67
use RESTAPI\Models\FirewallAlias;
78

89
class APIModelsFirewallAliasTestCase extends TestCase {
910
/**
1011
* Checks that aliases with hostnames correctly populate a pfctl table
1112
*/
12-
public function test_fqdn_alias_populates_pfctl_table() {
13+
#[TestCaseRetry(retries: 3, delay: 1)]
14+
public function test_fqdn_alias_populates_pfctl_table(): void {
1315
# Create an alias that includes dns.google as an alias item
1416
$test_alias = new FirewallAlias(
1517
data: [

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class APIModelsTableTestCase extends TestCase {
8282
/**
8383
* Checks that we can successfully delete (flush) entrries from a table
8484
*/
85+
#[TestCaseRetry(retries: 3, delay: 1)]
8586
public function test_delete(): void {
8687
# Create a new pf table to test with
8788
$this->add_table(table_name: 'pfrest_test_table', entries: ['1.2.3.4', '4.3.2.1']);

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,45 @@ class APIModelsVirtualIPTestCase extends TestCase {
242242
$carp_status->update();
243243
$carp_vip->delete(apply: true);
244244
}
245+
246+
public function test_carp_vhid_must_be_unique_per_interface(): void {
247+
# Create a virtual IP to test with
248+
$vip = new VirtualIP(
249+
mode: 'carp',
250+
interface: 'lan',
251+
subnet: '127.1.2.3',
252+
subnet_bits: 32,
253+
password: 'testpasswd',
254+
vhid: 5,
255+
);
256+
$vip->create();
257+
258+
# Ensure we can update the existing VIP with the same VHID without issue
259+
$this->assert_does_not_throw(
260+
callable: function () use ($vip) {
261+
$vip->validate_vhid(vhid: 5);
262+
},
263+
);
264+
265+
# Ensure we cannot create a new VIP with the same VHID on the same interface
266+
$this->assert_throws_response(
267+
response_id: 'VIRTUALIP_VHID_ALREADY_IN_USE',
268+
code: 409,
269+
callable: function () {
270+
$vip = new VirtualIP(mode: 'carp', interface: 'lan');
271+
$vip->validate_vhid(vhid: 5);
272+
},
273+
);
274+
275+
# Ensure we can create a new VIP with the same VHID on a different interface
276+
$this->assert_does_not_throw(
277+
callable: function () {
278+
$vip = new VirtualIP(mode: 'carp', interface: 'wan');
279+
$vip->validate_vhid(vhid: 5);
280+
},
281+
);
282+
283+
# Clean up the VIP we created
284+
$vip->delete();
285+
}
245286
}

0 commit comments

Comments
 (0)