diff --git a/plist b/plist index 1db7df480..2a7da06ed 100644 --- a/plist +++ b/plist @@ -531,6 +531,7 @@ /usr/local/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/DescriptionField.php /usr/local/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/EmailField.php /usr/local/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/HostnameField.php +/usr/local/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/IPPortField.php /usr/local/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/IntegerField.php /usr/local/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/InterfaceField.php /usr/local/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/JsonKeyValueStoreField.php @@ -845,6 +846,7 @@ /usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/CertificateFieldTest/config.xml /usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/CountryFieldTest.php /usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/Field_Framework_TestCase.php +/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/IPPortFieldTest.php /usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/IntegerFieldTest.php /usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/InterfaceFieldTest.php /usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/InterfaceFieldTest/config.xml diff --git a/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/IPPortField.php b/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/IPPortField.php new file mode 100644 index 000000000..650db0b76 --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/IPPortField.php @@ -0,0 +1,149 @@ +internalAsList) { + // return result as list + $result = array(); + foreach (explode($this->internalFieldSeparator, $this->internalValue) as $address) { + $result[$address] = array("value" => $address, "selected" => 1); + } + return $result; + } else { + // normal, single field response + return $this->internalValue; + } + } + + /** + * setter for address family + * @param $value address family [ipv4, ipv6, empty for all] + */ + public function setAddressFamily($value) + { + $this->internalAddressFamily = trim(strtolower($value)); + } + + /** + * select if multiple IP-Port combinations may be selected at once + * @param $value string value Y/N + */ + public function setAsList($value) + { + $this->internalAsList = trim(strtoupper($value)) == "Y"; + } + + + /** + * {@inheritdoc} + */ + protected function defaultValidationMessage() + { + return gettext('Invalid IP-port combination.'); + } + + /** + * retrieve field validators for this field type + * @return array returns validators + */ + public function getValidators() + { + $validators = parent::getValidators(); + if ($this->internalValue != null) { + $validators[] = new CallbackValidator(["callback" => function ($data) { + foreach ($this->internalAsList ? explode($this->internalFieldSeparator, $data) : [$data] as $value) { + if ($this->internalAddressFamily == 'ipv4' || $this->internalAddressFamily == null) { + $parts = explode(':', $value); + if (count($parts) == 2 && Util::isIpv4Address($parts[0]) && Util::isPort($parts[1])) { + continue; + } + } + + if ($this->internalAddressFamily == 'ipv6' || $this->internalAddressFamily == null) { + $parts = preg_split('/\[([^\]]+)\]/', $value, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + if (count($parts) == 2 && + Util::isIpv6Address($parts[0]) && + str_contains($parts[1], ':') && + Util::isPort(trim($parts[1], ': '))) { + continue; + } + } + + return ["\"" . $value . "\" is invalid. " . $this->getValidationMessage()]; + } + }]); + } + + return $validators; + } +} \ No newline at end of file diff --git a/src/opnsense/mvc/app/models/OPNsense/Diagnostics/Netflow.xml b/src/opnsense/mvc/app/models/OPNsense/Diagnostics/Netflow.xml index a4b7f3373..7517fced7 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Diagnostics/Netflow.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Diagnostics/Netflow.xml @@ -24,10 +24,9 @@ v9 - - /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):(6553[0-5]|655[0-2][0-9]|65[0-4]\d{2}|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{0,3})$/u - Please enter valid targets (e.g. 192.168.0.1:2055). - Y + + Y + ipv4 diff --git a/src/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/IPPortFieldTest.php b/src/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/IPPortFieldTest.php new file mode 100644 index 000000000..729d852b9 --- /dev/null +++ b/src/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/IPPortFieldTest.php @@ -0,0 +1,152 @@ +assertInstanceOf('\OPNsense\Base\FieldTypes\IPPortField', new IPPortField()); + } + + public function testRequiredEmpty() + { + $this->expectException(\Phalcon\Filter\Validation\Exception::class); + $this->expectExceptionMessage("PresenceOf"); + $field = new IPPortField(); + $field->setRequired("Y"); + $field->setValue(""); + $this->validateThrow($field); + } + + public function testNotRequiredEmpty() + { + $field = new IPPortField(); + $field->setValue(""); + $this->assertEmpty($this->validate($field)); + } + + public function testRequiredNotEmpty() + { + $field = new IPPortField(); + $field->setRequired("Y"); + $field->setValue("127.0.0.1:2056"); + $this->assertEmpty($this->validate($field)); + } + + public function testValidValueIpv4() + { + $field = new IPPortField(); + $field->setValue("127.0.0.1:2056"); + $this->assertEmpty($this->validate($field)); + } + + public function testValidValueAsListIpv4() + { + $field = new IPPortField(); + $field->setAsList("Y"); + $field->setValue("127.0.0.1:2056,192.168.1.1:1111"); + $this->assertEmpty($this->validate($field)); + } + + public function testValidValueIpv6() + { + $field = new IPPortField(); + $field->setValue("[::1]:2056"); + $this->assertEmpty($this->validate($field)); + } + + public function testValidValueAsListIpv6() + { + $field = new IPPortField(); + $field->setAsList("Y"); + $field->setValue("[::1]:2056,[fe80::]:1111"); + $this->assertEmpty($this->validate($field)); + } + + public function testInvalidValueIpv4() + { + $this->expectException(\Phalcon\Filter\Validation\Exception::class); + $field = new IPPortField(); + $field->setValue("abcdefg"); + $this->validateThrow($field); + } + + public function testInvalidValueAsListIpv4() + { + $this->expectException(\Phalcon\Filter\Validation\Exception::class); + $field = new IPPortField(); + $field->setAsList("Y"); + $field->setValue("127.0.0.1:2056,abcdefg"); + $this->validateThrow($field); + } + + public function testInvalidValueIpv6() + { + $this->expectException(\Phalcon\Filter\Validation\Exception::class); + $field = new IPPortField(); + $field->setValue("[::1]"); + $this->validateThrow($field); + } + + public function testInvalidValueAsListIpv6() + { + $this->expectException(\Phalcon\Filter\Validation\Exception::class); + $field = new IPPortField(); + $field->setAsList("Y"); + $field->setValue("[::1]:2056,[fe80::]"); + $this->validateThrow($field); + } + + public function testAddressFamilyIpv4() + { + $this->expectException(\Phalcon\Filter\Validation\Exception::class); + $field = new IPPortField(); + $field->setAddressFamily("ipv4"); + $field->setValue("[::1]:2056"); + $this->validateThrow($field); + } + + public function testAddressFamilyIpv6() + { + $this->expectException(\Phalcon\Filter\Validation\Exception::class); + $field = new IPPortField(); + $field->setAddressFamily("ipv6"); + $field->setValue("192.168.1.1:1111"); + $this->validateThrow($field); + } +}