Extend PortField to support multiple items, closes https://github.com/opnsense/core/issues/3895

- while here fix unit tests to support 8.5.x
- add unit test for PortField
This commit is contained in:
Ad Schellevis 2020-01-28 15:41:29 +01:00
parent 82c919ec77
commit c1c32c9eed
14 changed files with 212 additions and 97 deletions

View File

@ -300,7 +300,7 @@ class Config extends Singleton
}
// in case there are no backups, restore defaults.
$logger->error(gettext('No valid config.xml found, attempting to restore factory config.'));
$this->restoreBackup('/usr/local/etc/config.xml');
$this->restoreBackup('/usr/local/etc/config.xml.sample');
}
}

View File

@ -1,7 +1,7 @@
<?php
/*
* Copyright (C) 2015 Deciso B.V.
* Copyright (C) 2015-2020 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -29,19 +29,13 @@
namespace OPNsense\Base\FieldTypes;
use Phalcon\Validation\Validator\InclusionIn;
use OPNsense\Base\Validators\CallbackValidator;
/**
* Class PortField field type for ports, includes validation for services in /etc/services or valid number ranges.
* @package OPNsense\Base\FieldTypes
*/
class PortField extends BaseField
class PortField extends BaseListField
{
/**
* @var bool marks if this is a data node or a container
*/
protected $internalIsContainer = false;
/**
* @var array list of well known services
*/
@ -103,24 +97,19 @@ class PortField extends BaseField
*/
private $enableRanges = false;
/**
* @var array collected options
*/
private static $internalOptionList = null;
/**
* generate validation data (list of port numbers and well know ports)
*/
protected function actionPostLoadingEvent()
{
if (!is_array(self::$internalOptionList)) {
if ($this->enableWellKown) {
self::$internalOptionList = array("any") + self::$wellknownservices;
if ($this->enableWellKown) {
foreach (array("any") + self::$wellknownservices as $wellknown) {
$this->internalOptionList[(string)$wellknown] = $wellknown;
}
}
for ($port = 1; $port <= 65535; $port++) {
self::$internalOptionList[] = (string)$port;
}
for ($port = 1; $port <= 65535; $port++) {
$this->internalOptionList[(string)$port] = (string)$port;
}
}
@ -173,35 +162,24 @@ class PortField extends BaseField
*/
public function getValidators()
{
$validators = parent::getValidators();
if (
($this->internalIsRequired == true || $this->internalValue != null) &&
count(self::$internalOptionList) > 0
) {
if (count(explode("-", $this->internalValue)) == 2 && $this->enableRanges) {
// range validation
$validators[] = new CallbackValidator(["callback" => function ($data) {
$messages = [];
if ($this->enableRanges) {
// add valid ranges to options
foreach (explode(",", $this->internalValue) as $data) {
if (strpos($data, "-") !== false) {
$tmp = explode('-', $data);
foreach ($tmp as $port) {
if (
filter_var(
$port,
FILTER_VALIDATE_INT,
array('options' => array('min_range' => 1, 'max_range' => 65535))
) === false
if (count($tmp) == 2) {
if (filter_var($tmp[0],FILTER_VALIDATE_INT,
array('options' => array('min_range' => 1, 'max_range' => 65535))) !== false &&
filter_var($tmp[1],FILTER_VALIDATE_INT,
array('options' => array('min_range' => 1, 'max_range' => 65535))) !== false &&
$tmp[0] < $tmp[1]
) {
$messages[] = $this->getValidationMessage();
break;
$this->internalOptionList[$data] = $data;
}
}
return $messages;
}]);
} else {
$validators[] = new InclusionIn(array('message' => $this->getValidationMessage(),
'domain' => self::$internalOptionList));
}
}
}
return $validators;
return parent::getValidators();
}
}

View File

@ -40,13 +40,13 @@ class MonitTest extends \PHPUnit\Framework\TestCase
// holds the SettingsController object
protected static $setMonit;
protected function setUp()
protected function setUp(): void
{
// XXX: Unit test has dependencies, which are not handled within the same test suite.
$this->markTestIncomplete();
}
public static function setUpBeforeClass()
public static function setUpBeforeClass(): void
{
self::$setMonit = new \OPNsense\Monit\Api\SettingsController();
}

View File

@ -88,12 +88,12 @@ class BaseModelTest extends \PHPUnit\Framework\TestCase
}
/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage FromEmailXXX not an attribute of general
* @depends testRunMigrations
*/
public function testCannotSetNonExistingField()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage("FromEmailXXX not an attribute of general");
BaseModelTest::$model->general->FromEmailXXX = "test@test.nl";
}
@ -167,11 +167,11 @@ class BaseModelTest extends \PHPUnit\Framework\TestCase
/**
* @depends testCanAssignArrayType
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage not a valid number
*/
public function testValidationNOK()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("not a valid number");
// replace all numbers
foreach (BaseModelTest::$model->arraytypes->item->iterateItems() as $nodeid => $node) {
$node->number = "XXX";
@ -241,11 +241,11 @@ class BaseModelTest extends \PHPUnit\Framework\TestCase
/**
* @depends testGetNodes
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage number should be unique
*/
public function testConstraintsNok()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("number should be unique");
$count = 2;
foreach (BaseModelTest::$model->arraytypes->item->iterateItems() as $nodeid => $node) {
$count--;
@ -285,12 +285,12 @@ class BaseModelTest extends \PHPUnit\Framework\TestCase
}
/**
* @expectedException Exception
* @expectedExceptionMessage All fields should contain data or none of them
* @depends testAllOrNoneInitial
*/
public function testAllOrNoneNok()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessage("All fields should contain data or none of them");
BaseModelTest::$model->AllOrNone->value1 = "";
BaseModelTest::$model->AllOrNone->value2 = "X";
BaseModelTest::$model->AllOrNone->value3 = "";
@ -327,12 +327,12 @@ class BaseModelTest extends \PHPUnit\Framework\TestCase
}
/**
* @expectedException Exception
* @expectedExceptionMessage Only one option could be selected
* @depends testSingleSelectInitial
*/
public function testSingleSelectNok()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessage("Only one option could be selected");
BaseModelTest::$model->SingleSelect->value1 = "x";
BaseModelTest::$model->SingleSelect->value2 = "x";
BaseModelTest::$model->SingleSelect->value3 = "";
@ -371,11 +371,11 @@ class BaseModelTest extends \PHPUnit\Framework\TestCase
/**
* @depends testDependConstraintInitial
* @expectedException Exception
* @expectedExceptionMessage when value1 is enabled value2 is required
*/
public function testDependConstraintNok()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessage("when value1 is enabled value2 is required");
BaseModelTest::$model->DependConstraint->value1 = "1";
BaseModelTest::$model->DependConstraint->value2 = "";
BaseModelTest::$model->serializeToConfig();

View File

@ -68,11 +68,11 @@ class AuthGroupFieldTest extends Field_Framework_TestCase
/**
* @depends testCanBeCreated
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage CsvListValidator
*/
public function testSelectSetWithUnknownValue()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("CsvListValidator");
// init field
$field = new AuthGroupField();
$field->eventPostLoading();
@ -97,11 +97,11 @@ class AuthGroupFieldTest extends Field_Framework_TestCase
/**
* @depends testCanBeCreated
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage InclusionIn
*/
public function testSelectSetOnSingleValue()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("InclusionIn");
// init field
$field = new AuthGroupField();
$field->eventPostLoading();

View File

@ -81,11 +81,11 @@ class AuthenticationServerFieldTest extends Field_Framework_TestCase
/**
* @depends testCanBeCreated
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage CsvListValidator
*/
public function testSelectSetWithUnknownValue()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("CsvListValidator");
// init field
$field = new AuthenticationServerField();
$field->eventPostLoading();
@ -110,11 +110,11 @@ class AuthenticationServerFieldTest extends Field_Framework_TestCase
/**
* @depends testCanBeCreated
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage InclusionIn
*/
public function testSelectSetOnSingleValue()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("InclusionIn");
// init field
$field = new AuthenticationServerField();
$field->eventPostLoading();

View File

@ -48,22 +48,22 @@ class BooleanFieldTest extends Field_Framework_TestCase
}
/**
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage Regex
*/
public function testShouldNotBeANumber()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("Regex");
$field = new BooleanField();
$field->setValue("90");
$this->validateThrow($field);
}
/**
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage Regex
*/
public function testShouldNotBeAString()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("Regex");
$field = new BooleanField();
$field->setValue("xx");
$this->validateThrow($field);
@ -71,11 +71,11 @@ class BooleanFieldTest extends Field_Framework_TestCase
/**
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage PresenceOf
*/
public function testRequiredEmpty()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("PresenceOf");
$field = new BooleanField();
$field->setRequired("Y");
$field->setValue("");

View File

@ -81,11 +81,11 @@ class CertificateFieldTest extends Field_Framework_TestCase
/**
* @depends testCanBeCreated
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage CsvListValidator
*/
public function testSelectSetWithUnknownValue()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("CsvListValidator");
// init field
$field = new CertificateField();
$field->eventPostLoading();
@ -110,11 +110,11 @@ class CertificateFieldTest extends Field_Framework_TestCase
/**
* @depends testCanBeCreated
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage InclusionIn
*/
public function testSelectSetOnSingleValue()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("InclusionIn");
// init field
$field = new CertificateField();
$field->eventPostLoading();

View File

@ -48,11 +48,11 @@ class CountryFieldTest extends Field_Framework_TestCase
}
/**
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage PresenceOf
*/
public function testRequiredEmpty()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("PresenceOf");
$field = new CountryField();
$field->eventPostLoading();
$field->setRequired("Y");
@ -99,11 +99,11 @@ class CountryFieldTest extends Field_Framework_TestCase
}
/**
* @depends testCanBeCreated
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage CsvListValidator
*/
public function testSelectSetWithUnknownValue()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("CsvListValidator");
// init field
$field = new CountryField();
$field->eventPostLoading();
@ -128,11 +128,11 @@ class CountryFieldTest extends Field_Framework_TestCase
/**
* @depends testCanBeCreated
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage InclusionIn
*/
public function testSelectSetOnSingleValue()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("InclusionIn");
// init field
$field = new CountryField();
$field->eventPostLoading();

View File

@ -48,11 +48,11 @@ class IntegerFieldTest extends Field_Framework_TestCase
}
/**
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage MinMaxValidator
*/
public function testValueLargerThenMax()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("MinMaxValidator");
$field = new IntegerField();
$field->setMaximumValue(100);
$field->setMinimumValue(10);
@ -62,11 +62,11 @@ class IntegerFieldTest extends Field_Framework_TestCase
}
/**
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage MinMaxValidator
*/
public function testValueSmallerThenMin()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("MinMaxValidator");
$field = new IntegerField();
$field->setMaximumValue(100);
$field->setMinimumValue(10);
@ -102,11 +102,11 @@ class IntegerFieldTest extends Field_Framework_TestCase
}
/**
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage PresenceOf
*/
public function testRequiredEmpty()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("PresenceOf");
$field = new IntegerField();
$field->setRequired("Y");
$field->setValue("");

View File

@ -68,11 +68,11 @@ class InterfaceFieldTest extends Field_Framework_TestCase
/**
* @depends testCanBeCreated
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage InclusionIn
*/
public function testSelectHasNoParents()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("InclusionIn");
// init field
$field = new InterfaceField();
$field->eventPostLoading();
@ -109,11 +109,11 @@ class InterfaceFieldTest extends Field_Framework_TestCase
/**
* @depends testCanBeCreated
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage CsvListValidator
*/
public function testSelectSetWithUnknownValue()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("CsvListValidator");
// init field
$field = new InterfaceField();
$field->eventPostLoading();
@ -138,11 +138,11 @@ class InterfaceFieldTest extends Field_Framework_TestCase
/**
* @depends testCanBeCreated
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage InclusionIn
*/
public function testSelectSetOnSingleValue()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("InclusionIn");
// init field
$field = new InterfaceField();
$field->eventPostLoading();

View File

@ -39,7 +39,7 @@ use \OPNsense\Core\Config;
class ModelRelationFieldTest extends Field_Framework_TestCase
{
protected function setUp()
protected function setUp(): void
{
FactoryDefault::getDefault()->get('config')->globals->config_path = __DIR__ . '/ModelRelationFieldTest/';
Config::getInstance()->forceReload();

View File

@ -48,11 +48,11 @@ class OptionFieldTest extends Field_Framework_TestCase
}
/**
* @expectedException \Phalcon\Validation\Exception
* @expectedExceptionMessage PresenceOf
*/
public function testRequiredEmpty()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("PresenceOf");
$field = new OptionField();
$field->setRequired("Y");
$field->setValue("");

View File

@ -0,0 +1,137 @@
<?php
/**
* Copyright (C) 2020 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\PortField;
class PortFieldTest extends Field_Framework_TestCase
{
/**
* test construct
*/
public function testCanBeCreated()
{
$this->assertInstanceOf('\OPNsense\Base\FieldTypes\PortField', new PortField());
}
/**
*/
public function testRequiredEmpty()
{
$this->expectException(\Phalcon\Validation\Exception::class);
$this->expectExceptionMessage("PresenceOf");
$field = new PortField();
$field->setRequired("Y");
$field->setValue("");
$field->eventPostLoading();
$this->validateThrow($field);
}
/**
* required not empty
*/
public function testRequiredNotEmpty()
{
$field = new PortField();
$field->setRequired("Y");
$field->setValue("80");
$field->eventPostLoading();
$this->assertEmpty($this->validate($field));
}
/**
* required not empty
*/
public function testValidValues()
{
$field = new PortField();
$field->setEnableRanges("Y");
$field->setEnableWellKnown("Y");
$field->eventPostLoading();
foreach (array("80", "443", "https", "80-100") as $value) {
$field->setValue($value);
$this->assertEmpty($this->validate($field));
}
}
/**
* all items valid
*/
public function testValidValueList()
{
$field = new PortField();
$field->setEnableRanges("Y");
$field->setEnableWellKnown("Y");
$field->setMultiple("Y");
$field->eventPostLoading();
$field->setValue("80,443,https,80-100");
$this->assertEmpty($this->validate($field));
}
/**
* range not expected
*/
public function testRangeNotExpected()
{
$field = new PortField();
$field->setEnableWellKnown("Y");
$field->setMultiple("Y");
$field->eventPostLoading();
$field->setValue("80;443;https;80-100");
$this->assertNotEmpty($this->validate($field));
}
/**
* required not empty
*/
public function testInValidValues()
{
$field = new PortField();
foreach (array("x1", "x2", "999999-88888888") as $value) {
$field->setValue($value);
$this->assertNotEmpty($this->validate($field));
}
}
/**
* type is not a container
*/
public function testIsContainer()
{
$field = new PortField();
$this->assertFalse($field->isContainer());
}
}