mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-13 00:07:26 +00:00
mvc: merge NetworkValidator into NetworkField to ease extensibility and add unit test
also needed for https://github.com/opnsense/core/issues/8329
This commit is contained in:
parent
bb37fa89e7
commit
c93e0ac745
2
plist
2
plist
@ -673,7 +673,6 @@
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/Email.php
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/IntegerValidator.php
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/MinMaxValidator.php
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/NetworkValidator.php
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/Numericality.php
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/PresenceOf.php
|
||||
/usr/local/opnsense/mvc/app/models/OPNsense/Base/Validators/Regex.php
|
||||
@ -1042,6 +1041,7 @@
|
||||
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/ModelRelationFieldTest/config.xml
|
||||
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/NetworkAliasFieldTest.php
|
||||
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/NetworkAliasFieldTest/config.xml
|
||||
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/NetworkFieldTest.php
|
||||
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/OptionFieldTest.php
|
||||
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/PortFieldTest.php
|
||||
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/ProtocolFieldTest.php
|
||||
|
||||
@ -28,7 +28,8 @@
|
||||
|
||||
namespace OPNsense\Base\FieldTypes;
|
||||
|
||||
use OPNsense\Base\Validators\NetworkValidator;
|
||||
use OPNsense\Firewall\Util;
|
||||
use OPNsense\Base\Validators\CallbackValidator;
|
||||
|
||||
/**
|
||||
* @package OPNsense\Base\FieldTypes
|
||||
@ -190,24 +191,90 @@ class NetworkField extends BaseField
|
||||
return gettext('Please specify a valid network segment or IP address.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $input data to test
|
||||
* @return bool if valid network address or segment (using this objects settings)
|
||||
*/
|
||||
protected function isValidInput($input)
|
||||
{
|
||||
$result = true;
|
||||
if ($this->internalFieldSeparator == null) {
|
||||
$values = [$input];
|
||||
} else {
|
||||
$values = explode($this->internalFieldSeparator, $input);
|
||||
}
|
||||
foreach ($values as $value) {
|
||||
// parse filter options
|
||||
$filterOpt = 0;
|
||||
switch (strtolower($this->internalAddressFamily ?? '')) {
|
||||
case "ipv4":
|
||||
$filterOpt |= FILTER_FLAG_IPV4;
|
||||
break;
|
||||
case "ipv6":
|
||||
$filterOpt |= FILTER_FLAG_IPV6;
|
||||
break;
|
||||
default:
|
||||
$filterOpt |= FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6;
|
||||
}
|
||||
|
||||
// split network
|
||||
if (strpos($value, "/") !== false) {
|
||||
if ($this->internalNetMaskAllowed === false) {
|
||||
return false;
|
||||
} else {
|
||||
$cidr = $value;
|
||||
$parts = explode("/", $value);
|
||||
if (count($parts) > 2 || !ctype_digit($parts[1])) {
|
||||
// more parts then expected or second part is not numeric
|
||||
return false;
|
||||
} else {
|
||||
$mask = $parts[1];
|
||||
$value = $parts[0];
|
||||
if (strpos($parts[0], ":") !== false) {
|
||||
// probably ipv6, mask must be between 0..128
|
||||
if ($mask < 0 || $mask > 128) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// most likely ipv4 address, mask must be between 0..32
|
||||
if ($mask < 0 || $mask > 32) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->internalStrict === true && !Util::isSubnetStrict($cidr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} elseif ($this->internalNetMaskRequired === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter_var($value, FILTER_VALIDATE_IP, $filterOpt) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;;
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve field validators for this field type
|
||||
* @return array returns Text/regex validator
|
||||
* @return array returns validators
|
||||
*/
|
||||
public function getValidators()
|
||||
{
|
||||
$validators = parent::getValidators();
|
||||
if ($this->internalValue != null) {
|
||||
if ($this->internalValue != "any" || $this->internalWildcardEnabled == false) {
|
||||
// accept any as target
|
||||
$validators[] = new NetworkValidator([
|
||||
'message' => $this->getValidationMessage(),
|
||||
'split' => $this->internalFieldSeparator,
|
||||
'netMaskRequired' => $this->internalNetMaskRequired,
|
||||
'netMaskAllowed' => $this->internalNetMaskAllowed,
|
||||
'version' => $this->internalAddressFamily,
|
||||
'strict' => $this->internalStrict
|
||||
]);
|
||||
$that = $this;
|
||||
$validators[] = new CallbackValidator(["callback" => function ($data) use ($that) {
|
||||
$messages = [];
|
||||
if (!$that->isValidInput($data)) {
|
||||
$messages[] = $this->getValidationMessage();
|
||||
}
|
||||
return $messages;
|
||||
}]);
|
||||
}
|
||||
}
|
||||
return $validators;
|
||||
|
||||
@ -1,133 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace OPNsense\Base\Validators;
|
||||
|
||||
use OPNsense\Base\BaseValidator;
|
||||
use OPNsense\Firewall\Util;
|
||||
use OPNsense\Base\Messages\Message;
|
||||
|
||||
/**
|
||||
* Class NetworkValidator validate networks and ip addresses
|
||||
* @package OPNsense\Base\Validators
|
||||
*/
|
||||
class NetworkValidator extends BaseValidator
|
||||
{
|
||||
/**
|
||||
* Executes network / ip validation, accepts the following parameters as attributes:
|
||||
* version : ipv4, ipv6, all (default)
|
||||
* noReserved : true, false (default)
|
||||
* noPrivate : true, false (default)
|
||||
* noSubnet : true, false (default)
|
||||
* netMaskRequired : true, false (default)
|
||||
* strict: : true, false (default)
|
||||
*
|
||||
* @param $validator
|
||||
* @param string $attribute
|
||||
* @return boolean
|
||||
*/
|
||||
public function validate($validator, $attribute): bool
|
||||
{
|
||||
$result = true;
|
||||
$msg = $this->getOption('message');
|
||||
$fieldSplit = $this->getOption('split', null);
|
||||
if ($fieldSplit == null) {
|
||||
$values = array($validator->getValue($attribute));
|
||||
} else {
|
||||
$values = explode($fieldSplit, $validator->getValue($attribute));
|
||||
}
|
||||
foreach ($values as $value) {
|
||||
// parse filter options
|
||||
$filterOpt = 0;
|
||||
switch (strtolower($this->getOption('version') ?? '')) {
|
||||
case "ipv4":
|
||||
$filterOpt |= FILTER_FLAG_IPV4;
|
||||
break;
|
||||
case "ipv6":
|
||||
$filterOpt |= FILTER_FLAG_IPV6;
|
||||
break;
|
||||
default:
|
||||
$filterOpt |= FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6;
|
||||
}
|
||||
|
||||
if ($this->getOption('noReserved') === true) {
|
||||
$filterOpt |= FILTER_FLAG_NO_RES_RANGE;
|
||||
}
|
||||
|
||||
if ($this->getOption('noPrivate') === true) {
|
||||
$filterOpt |= FILTER_FLAG_NO_PRIV_RANGE;
|
||||
}
|
||||
|
||||
// split network
|
||||
if (strpos($value, "/") !== false) {
|
||||
if ($this->getOption('netMaskAllowed') === false) {
|
||||
$result = false;
|
||||
} else {
|
||||
$cidr = $value;
|
||||
$parts = explode("/", $value);
|
||||
if (count($parts) > 2 || !ctype_digit($parts[1])) {
|
||||
// more parts then expected or second part is not numeric
|
||||
$result = false;
|
||||
} else {
|
||||
$mask = $parts[1];
|
||||
$value = $parts[0];
|
||||
if (strpos($parts[0], ":") !== false) {
|
||||
// probably ipv6, mask must be between 0..128
|
||||
if ($mask < 0 || $mask > 128) {
|
||||
$result = false;
|
||||
}
|
||||
} else {
|
||||
// most likely ipv4 address, mask must be between 0..32
|
||||
if ($mask < 0 || $mask > 32) {
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getOption('strict') === true && !Util::isSubnetStrict($cidr)) {
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
} elseif ($this->getOption('netMaskRequired') === true) {
|
||||
$result = false;
|
||||
}
|
||||
|
||||
|
||||
if (filter_var($value, FILTER_VALIDATE_IP, $filterOpt) === false) {
|
||||
$result = false;
|
||||
}
|
||||
|
||||
if (!$result) {
|
||||
// append validation message
|
||||
$validator->appendMessage(new Message($msg, $attribute, 'NetworkValidator'));
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2019 Deciso B.V.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace tests\OPNsense\Base\FieldTypes;
|
||||
|
||||
// @CodingStandardsIgnoreStart
|
||||
require_once 'Field_Framework_TestCase.php';
|
||||
// @CodingStandardsIgnoreEnd
|
||||
|
||||
use OPNsense\Base\FieldTypes\NetworkField;
|
||||
|
||||
class NetworkFieldTest extends Field_Framework_TestCase
|
||||
{
|
||||
public function testCanBeCreated()
|
||||
{
|
||||
$this->assertInstanceOf('\OPNsense\Base\FieldTypes\NetworkField', new NetworkField());
|
||||
}
|
||||
|
||||
public function testRequiredEmpty()
|
||||
{
|
||||
$this->expectException(\OPNsense\Base\ValidationException::class);
|
||||
$this->expectExceptionMessage("PresenceOf");
|
||||
$field = new NetworkField();
|
||||
$field->setRequired("Y");
|
||||
$field->setValue("");
|
||||
$this->validateThrow($field);
|
||||
}
|
||||
|
||||
public function testRequiredNotEmpty()
|
||||
{
|
||||
$field = new NetworkField();
|
||||
$field->setRequired("Y");
|
||||
$field->setValue("192.168.1.1");
|
||||
$this->assertEmpty($this->validate($field));
|
||||
}
|
||||
|
||||
public function testValidValuesV4()
|
||||
{
|
||||
$field = new NetworkField();
|
||||
$field->setNetMaskRequired("Y");
|
||||
$field->setAddressFamily("ipv4");
|
||||
foreach (["192.168.1.1/24", "10.0.0.1/24"] as $value) {
|
||||
$field->setValue($value);
|
||||
$this->assertEmpty($this->validate($field));
|
||||
}
|
||||
}
|
||||
|
||||
public function testValidValuesV6()
|
||||
{
|
||||
$field = new NetworkField();
|
||||
$field->setNetMaskRequired("Y");
|
||||
$field->setAddressFamily("ipv6");
|
||||
foreach (["2000::1/128", "fe80::5a8c:fcff:0001:ffe2/64"] as $value) {
|
||||
$field->setValue($value);
|
||||
$this->assertEmpty($this->validate($field));
|
||||
}
|
||||
}
|
||||
|
||||
public function testInValidValuesV4()
|
||||
{
|
||||
$field = new NetworkField();
|
||||
$field->setNetMaskRequired("Y");
|
||||
$field->setAddressFamily("ipv4");
|
||||
foreach (["192.168.1.1", "2000::1", "A"] as $value) {
|
||||
$field->setValue($value);
|
||||
$this->assertNotEmpty($this->validate($field));
|
||||
}
|
||||
}
|
||||
|
||||
public function testInValidValuesV6()
|
||||
{
|
||||
$field = new NetworkField();
|
||||
$field->setNetMaskRequired("Y");
|
||||
$field->setAddressFamily("ipv6");
|
||||
foreach (["192.168.1.1", "2000::1", "A"] as $value) {
|
||||
$field->setValue($value);
|
||||
$this->assertNotEmpty($this->validate($field));
|
||||
}
|
||||
}
|
||||
|
||||
public function testValidValuesStrict()
|
||||
{
|
||||
$field = new NetworkField();
|
||||
$field->setNetMaskRequired("Y");
|
||||
$field->setStrict("Y");
|
||||
foreach (["192.168.1.0/24", "2000::0/64"] as $value) {
|
||||
$field->setValue($value);
|
||||
$this->assertEmpty($this->validate($field));
|
||||
}
|
||||
}
|
||||
|
||||
public function testInValidValuesStrict()
|
||||
{
|
||||
$field = new NetworkField();
|
||||
$field->setNetMaskRequired("Y");
|
||||
$field->setStrict("Y");
|
||||
foreach (["192.168.1.1/24", "2000::1:1/64"] as $value) {
|
||||
$field->setValue($value);
|
||||
$this->assertNotEmpty($this->validate($field));
|
||||
}
|
||||
}
|
||||
|
||||
public function testIsContainer()
|
||||
{
|
||||
$field = new NetworkField();
|
||||
$this->assertFalse($field->isContainer());
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user