Skip to content

Commit c93e0ac

Browse files
committed
mvc: merge NetworkValidator into NetworkField to ease extensibility and add unit test
also needed for opnsense#8329
1 parent bb37fa8 commit c93e0ac

File tree

4 files changed

+214
-145
lines changed

4 files changed

+214
-145
lines changed

plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,6 @@
673673
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/Email.php
674674
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/IntegerValidator.php
675675
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/MinMaxValidator.php
676-
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/NetworkValidator.php
677676
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/Numericality.php
678677
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/PresenceOf.php
679678
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/Regex.php
@@ -1042,6 +1041,7 @@
10421041
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/ModelRelationFieldTest/config.xml
10431042
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/NetworkAliasFieldTest.php
10441043
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/NetworkAliasFieldTest/config.xml
1044+
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/NetworkFieldTest.php
10451045
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/OptionFieldTest.php
10461046
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/PortFieldTest.php
10471047
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/ProtocolFieldTest.php

src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/NetworkField.php

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828

2929
namespace OPNsense\Base\FieldTypes;
3030

31-
use OPNsense\Base\Validators\NetworkValidator;
31+
use OPNsense\Firewall\Util;
32+
use OPNsense\Base\Validators\CallbackValidator;
3233

3334
/**
3435
* @package OPNsense\Base\FieldTypes
@@ -190,24 +191,90 @@ protected function defaultValidationMessage()
190191
return gettext('Please specify a valid network segment or IP address.');
191192
}
192193

194+
/**
195+
* @param string $input data to test
196+
* @return bool if valid network address or segment (using this objects settings)
197+
*/
198+
protected function isValidInput($input)
199+
{
200+
$result = true;
201+
if ($this->internalFieldSeparator == null) {
202+
$values = [$input];
203+
} else {
204+
$values = explode($this->internalFieldSeparator, $input);
205+
}
206+
foreach ($values as $value) {
207+
// parse filter options
208+
$filterOpt = 0;
209+
switch (strtolower($this->internalAddressFamily ?? '')) {
210+
case "ipv4":
211+
$filterOpt |= FILTER_FLAG_IPV4;
212+
break;
213+
case "ipv6":
214+
$filterOpt |= FILTER_FLAG_IPV6;
215+
break;
216+
default:
217+
$filterOpt |= FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6;
218+
}
219+
220+
// split network
221+
if (strpos($value, "/") !== false) {
222+
if ($this->internalNetMaskAllowed === false) {
223+
return false;
224+
} else {
225+
$cidr = $value;
226+
$parts = explode("/", $value);
227+
if (count($parts) > 2 || !ctype_digit($parts[1])) {
228+
// more parts then expected or second part is not numeric
229+
return false;
230+
} else {
231+
$mask = $parts[1];
232+
$value = $parts[0];
233+
if (strpos($parts[0], ":") !== false) {
234+
// probably ipv6, mask must be between 0..128
235+
if ($mask < 0 || $mask > 128) {
236+
return false;
237+
}
238+
} else {
239+
// most likely ipv4 address, mask must be between 0..32
240+
if ($mask < 0 || $mask > 32) {
241+
return false;
242+
}
243+
}
244+
}
245+
246+
if ($this->internalStrict === true && !Util::isSubnetStrict($cidr)) {
247+
return false;
248+
}
249+
}
250+
} elseif ($this->internalNetMaskRequired === true) {
251+
return false;
252+
}
253+
254+
if (filter_var($value, FILTER_VALIDATE_IP, $filterOpt) === false) {
255+
return false;
256+
}
257+
}
258+
return true;;
259+
}
260+
193261
/**
194262
* retrieve field validators for this field type
195-
* @return array returns Text/regex validator
263+
* @return array returns validators
196264
*/
197265
public function getValidators()
198266
{
199267
$validators = parent::getValidators();
200268
if ($this->internalValue != null) {
201269
if ($this->internalValue != "any" || $this->internalWildcardEnabled == false) {
202-
// accept any as target
203-
$validators[] = new NetworkValidator([
204-
'message' => $this->getValidationMessage(),
205-
'split' => $this->internalFieldSeparator,
206-
'netMaskRequired' => $this->internalNetMaskRequired,
207-
'netMaskAllowed' => $this->internalNetMaskAllowed,
208-
'version' => $this->internalAddressFamily,
209-
'strict' => $this->internalStrict
210-
]);
270+
$that = $this;
271+
$validators[] = new CallbackValidator(["callback" => function ($data) use ($that) {
272+
$messages = [];
273+
if (!$that->isValidInput($data)) {
274+
$messages[] = $this->getValidationMessage();
275+
}
276+
return $messages;
277+
}]);
211278
}
212279
}
213280
return $validators;

src/opnsense/mvc/app/models/OPNsense/Base/Validators/NetworkValidator.php

Lines changed: 0 additions & 133 deletions
This file was deleted.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
3+
/**
4+
* Copyright (C) 2019 Deciso B.V.
5+
*
6+
* All rights reserved.
7+
*
8+
* Redistribution and use in source and binary forms, with or without
9+
* modification, are permitted provided that the following conditions are met:
10+
*
11+
* 1. Redistributions of source code must retain the above copyright notice,
12+
* this list of conditions and the following disclaimer.
13+
*
14+
* 2. Redistributions in binary form must reproduce the above copyright
15+
* notice, this list of conditions and the following disclaimer in the
16+
* documentation and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19+
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20+
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21+
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22+
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27+
* POSSIBILITY OF SUCH DAMAGE.
28+
*
29+
*/
30+
31+
namespace tests\OPNsense\Base\FieldTypes;
32+
33+
// @CodingStandardsIgnoreStart
34+
require_once 'Field_Framework_TestCase.php';
35+
// @CodingStandardsIgnoreEnd
36+
37+
use OPNsense\Base\FieldTypes\NetworkField;
38+
39+
class NetworkFieldTest extends Field_Framework_TestCase
40+
{
41+
public function testCanBeCreated()
42+
{
43+
$this->assertInstanceOf('\OPNsense\Base\FieldTypes\NetworkField', new NetworkField());
44+
}
45+
46+
public function testRequiredEmpty()
47+
{
48+
$this->expectException(\OPNsense\Base\ValidationException::class);
49+
$this->expectExceptionMessage("PresenceOf");
50+
$field = new NetworkField();
51+
$field->setRequired("Y");
52+
$field->setValue("");
53+
$this->validateThrow($field);
54+
}
55+
56+
public function testRequiredNotEmpty()
57+
{
58+
$field = new NetworkField();
59+
$field->setRequired("Y");
60+
$field->setValue("192.168.1.1");
61+
$this->assertEmpty($this->validate($field));
62+
}
63+
64+
public function testValidValuesV4()
65+
{
66+
$field = new NetworkField();
67+
$field->setNetMaskRequired("Y");
68+
$field->setAddressFamily("ipv4");
69+
foreach (["192.168.1.1/24", "10.0.0.1/24"] as $value) {
70+
$field->setValue($value);
71+
$this->assertEmpty($this->validate($field));
72+
}
73+
}
74+
75+
public function testValidValuesV6()
76+
{
77+
$field = new NetworkField();
78+
$field->setNetMaskRequired("Y");
79+
$field->setAddressFamily("ipv6");
80+
foreach (["2000::1/128", "fe80::5a8c:fcff:0001:ffe2/64"] as $value) {
81+
$field->setValue($value);
82+
$this->assertEmpty($this->validate($field));
83+
}
84+
}
85+
86+
public function testInValidValuesV4()
87+
{
88+
$field = new NetworkField();
89+
$field->setNetMaskRequired("Y");
90+
$field->setAddressFamily("ipv4");
91+
foreach (["192.168.1.1", "2000::1", "A"] as $value) {
92+
$field->setValue($value);
93+
$this->assertNotEmpty($this->validate($field));
94+
}
95+
}
96+
97+
public function testInValidValuesV6()
98+
{
99+
$field = new NetworkField();
100+
$field->setNetMaskRequired("Y");
101+
$field->setAddressFamily("ipv6");
102+
foreach (["192.168.1.1", "2000::1", "A"] as $value) {
103+
$field->setValue($value);
104+
$this->assertNotEmpty($this->validate($field));
105+
}
106+
}
107+
108+
public function testValidValuesStrict()
109+
{
110+
$field = new NetworkField();
111+
$field->setNetMaskRequired("Y");
112+
$field->setStrict("Y");
113+
foreach (["192.168.1.0/24", "2000::0/64"] as $value) {
114+
$field->setValue($value);
115+
$this->assertEmpty($this->validate($field));
116+
}
117+
}
118+
119+
public function testInValidValuesStrict()
120+
{
121+
$field = new NetworkField();
122+
$field->setNetMaskRequired("Y");
123+
$field->setStrict("Y");
124+
foreach (["192.168.1.1/24", "2000::1:1/64"] as $value) {
125+
$field->setValue($value);
126+
$this->assertNotEmpty($this->validate($field));
127+
}
128+
}
129+
130+
public function testIsContainer()
131+
{
132+
$field = new NetworkField();
133+
$this->assertFalse($field->isContainer());
134+
}
135+
}

0 commit comments

Comments
 (0)