Skip to content

Commit b7767bc

Browse files
fix(WireGuardTunnel): adjust 'addresses' validation pre-condition timing #902
The previous fix did not address the outer nested model field validation, only the inner validation which rendered the fix unusable for many use cases. This commits introduces a new injection point to update() that allows us to apply pre-conditions before model validation, thefor allow us to negate addresses before validation if an iface assignment exists.
1 parent 4190c8c commit b7767bc

3 files changed

Lines changed: 76 additions & 12 deletions

File tree

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Core/Model.inc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,13 @@ class Model {
18291829
$this->apply();
18301830
}
18311831

1832+
/**
1833+
* Initializes the 'pre_validate_update()' method. This method is intended to be overridden by a child model class
1834+
* and is called immediately before validating update actions. This is useful for conditions that need to be
1835+
* considered before validation occurs, such as scrubbing certain fields.
1836+
*/
1837+
protected function pre_validate_update(): void {}
1838+
18321839
/**
18331840
* Initializes the default 'pre_apply_update' method. This method is intended to be overridden by a child model class and
18341841
* is called immediately before the 'apply' method for update actions only. This method runs regardless of whether
@@ -2352,6 +2359,9 @@ class Model {
23522359
$this->remove_array_changes();
23532360
}
23542361

2362+
# Run the pre-validate method to allow for any adjustments to the object before validation occurs
2363+
$this->pre_validate_update();
2364+
23552365
# Ensure all object Fields and validations succeed for proceeding.
23562366
if ($this->validate(requires_id: true)) {
23572367
# When dry_run is enabled, skip the actual write/apply phase but still report the would-be result

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -145,18 +145,6 @@ class WireGuardTunnel extends Model {
145145
return wg_is_key_clamped($privatekey) ? $privatekey : wg_clamp_key($privatekey);
146146
}
147147

148-
/**
149-
* Extends the default _update method to ensure addresses are removed if the tunnel has an interface assignment
150-
*/
151-
public function _update(): void {
152-
# Remove any existing addresses if this tunnel has an existing interface assignment
153-
if (NetworkInterface::query(if: $this->name->value)->exists()) {
154-
$this->addresses->value = [];
155-
}
156-
157-
parent::_update();
158-
}
159-
160148
/**
161149
* Obtains the next available WireGuard tunnel interface name.
162150
* @return string The next available WireGuard tunnel interface name (i.e. tun_wg0)
@@ -165,6 +153,18 @@ class WireGuardTunnel extends Model {
165153
return next_wg_if();
166154
}
167155

156+
/**
157+
* Adds pre-validation logic to scrub addresses from this WireGuardTunnel if it has an existing interface
158+
* assignment.
159+
*/
160+
protected function pre_validate_update(): void {
161+
# If this tunnel is assigned to an existing pfSense interface, scrub any addresses from the `addresses` field
162+
# since the addresses for this tunnel will be derived from the assigned interface's configured IPs instead.
163+
if (NetworkInterface::query(if: $this->name->value)->exists()) {
164+
$this->addresses->value = [];
165+
}
166+
}
167+
168168
/**
169169
* Serializes changes to this tunnel before applying.
170170
*/

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,60 @@ class APIModelsWireGuardTunnelTestCase extends TestCase {
116116
$tun_wg0->delete(apply: true);
117117
}
118118

119+
/**
120+
* Checks that the `addresses` field is removed before an update when the tunnel has an existing interface
121+
* assignment, since addresses are derived from the assigned interface's configured IPs instead.
122+
*/
123+
public function test_addresses_removed_before_update_when_interface_is_assigned(): void {
124+
# Create a WireGuardTunnel to test with
125+
$tunnel = new WireGuardTunnel(privatekey: 'KG0BA4UyPilHH5qnXCfr6Lw8ynecOPor88tljLy3AHk=', async: false);
126+
$tunnel->create(apply: true);
127+
128+
# Assign a NetworkInterface for this tunnel
129+
$if = new NetworkInterface(
130+
if: $tunnel->name->value,
131+
descr: 'WGTEST',
132+
enable: true,
133+
typev4: 'none',
134+
typev6: 'none',
135+
async: false,
136+
);
137+
$if->create();
138+
139+
# Update the tunnel with addresses while it has an interface assignment
140+
# pre_validate_update() should scrub the addresses before validation runs
141+
$tunnel->from_representation(addresses: [['address' => '172.20.99.1', 'mask' => 30]]);
142+
$tunnel->update(apply: false);
143+
144+
# Ensure the addresses were removed before the update was processed
145+
$this->assert_is_empty($tunnel->addresses->value);
146+
147+
# Delete the interface and the tunnel
148+
$if->delete(apply: true);
149+
$tunnel->delete(apply: true);
150+
}
151+
152+
/**
153+
* Checks that the `addresses` field is left intact before an update when the tunnel has no existing
154+
* interface assignment.
155+
*/
156+
public function test_addresses_retained_on_update_when_no_interface_assigned(): void {
157+
# Create a WireGuardTunnel to test with
158+
$tunnel = new WireGuardTunnel(privatekey: 'KG0BA4UyPilHH5qnXCfr6Lw8ynecOPor88tljLy3AHk=', async: false);
159+
$tunnel->create(apply: true);
160+
161+
# Update the tunnel with addresses without an interface assignment
162+
# pre_validate_update() should leave the addresses intact
163+
$tunnel->from_representation(addresses: [['address' => '172.20.99.1', 'mask' => 30]]);
164+
$tunnel->update(apply: true);
165+
166+
# Ensure the addresses were retained during the update
167+
$this->assert_is_not_empty($tunnel->addresses->value);
168+
169+
# Delete the tunnel
170+
$tunnel->delete(apply: true);
171+
}
172+
119173
/**
120174
* Checks that the tunnel is properly configured on the backend after creating, updating and deleting
121175
*/

0 commit comments

Comments
 (0)