mirror of
https://github.com/LdesignMedia/moodle-availability_ipaddress.git
synced 2026-10-22 11:08:07 +02:00
Compare commits
9
Commits
d730ed97fd
..
5.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
197e5c3b58 | ||
|
|
06a5f9c8c9 | ||
|
|
150067f43d | ||
|
|
b1f0bc2f09 | ||
|
|
9269cb7f76 | ||
|
|
2210e85ca0 | ||
|
|
6b24f4a2b9 | ||
|
|
00941bbc54 | ||
|
|
cf5409707f |
No files matched your search
@@ -1,5 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
## 5.3.2 (2026101500)
|
||||
- Fixed IP address validation being too permissive https://github.com/LdesignMedia/moodle-availability_ipaddress/issues/2
|
||||
- Invalid entries like `2001:db8::1garbage` or `192.168.1.0/24garbage` are no longer accepted. Moodle silently ignored them, so a "must not" restriction could let blocked users in.
|
||||
- Only formats Moodle can match are accepted. Rejected: IPv6 written with an IPv4 part (`::ffff:192.168.1.1`), an IPv6 prefix without mask (`2001:db8::`, use `2001:db8::/32`), masks with leading zeros (`/024`) and some compressed IPv6 forms Moodle can't parse (`1::2:3:4:5:6:7`).
|
||||
- Predefined IP ranges are now validated with the same rules (subnet mask range, range end).
|
||||
- Spaces after commas are now allowed in the restriction field, e.g. `192.168.1.1, 10.0.0.1`.
|
||||
- Added support for IPv6 ranges in the last group, e.g. `2001:db8::1-ff`.
|
||||
- Note: existing restrictions and predefined ranges are not changed. If one contains an invalid entry, it now shows a validation error the next time it is edited. Entries that were already invalid never matched.
|
||||
|
||||
## 5.3.1 (2026101400)
|
||||
- Solved CI tests https://github.com/LdesignMedia/moodle-availability_ipaddress/issues/20 | Thanks @minhduchoang195
|
||||
|
||||
## 5.3.0 (2026092300)
|
||||
- Tested and verified on Moodle 5.3
|
||||
|
||||
## 5.2.0 (2026031802)
|
||||
- Tested and verified on Moodle 5.2
|
||||
|
||||
## 5.1.1 (2026031800)
|
||||
* Fixed PHP 7.3 compatibility issue that broke Moodle 4.0.x servers ([#13](https://github.com/LdesignMedia/moodle-availability_ipaddress/issues/13))
|
||||
* Removed typed properties (PHP 7.4+) to support PHP 7.3+
|
||||
|
||||
@@ -21,6 +21,8 @@ activity, making it accessible only to users from specified IP addresses.
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
namespace availability_ipaddress\form;
|
||||
|
||||
use availability_ipaddress\helper;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
@@ -102,52 +104,11 @@ class range_form extends \moodleform {
|
||||
public function validation($data, $files) {
|
||||
$errors = parent::validation($data, $files);
|
||||
|
||||
// Validate IP addresses.
|
||||
if (!empty($data['ipaddresses'])) {
|
||||
$ipaddresses = explode(',', $data['ipaddresses']);
|
||||
foreach ($ipaddresses as $ip) {
|
||||
$ip = trim($ip);
|
||||
if (!$this->validate_ip_format($ip)) {
|
||||
// Validate IP addresses, the field is required so an empty value is invalid as well.
|
||||
if (!helper::is_valid_ipaddress_list($data['ipaddresses'] ?? '')) {
|
||||
$errors['ipaddresses'] = get_string('error_ipaddress', 'availability_ipaddress');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate IP address format.
|
||||
*
|
||||
* @param string $ip
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validate_ip_format(string $ip): bool {
|
||||
|
||||
// Use the same validation logic as the main plugin.
|
||||
// This is a simplified version - you might want to use the same regex as in JS.
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for CIDR notation.
|
||||
if (strpos($ip, '/') !== false) {
|
||||
[$addr, $mask] = explode('/', $ip);
|
||||
if (filter_var($addr, FILTER_VALIDATE_IP) && is_numeric($mask)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for IP range.
|
||||
if (strpos($ip, '-') !== false) {
|
||||
[$start, $end] = explode('-', $ip);
|
||||
if (filter_var($start, FILTER_VALIDATE_IP)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -359,4 +359,141 @@ class helper {
|
||||
|
||||
return $modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a comma separated list of IP addresses.
|
||||
*
|
||||
* @param string $ipaddresses Comma separated list of IP addresses.
|
||||
*
|
||||
* @return bool True if every entry is valid.
|
||||
*/
|
||||
public static function is_valid_ipaddress_list(string $ipaddresses): bool {
|
||||
foreach (explode(',', $ipaddresses) as $entry) {
|
||||
if (!self::is_valid_ipaddress_entry(trim($entry))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a single IP address entry.
|
||||
*
|
||||
* Only formats that address_in_subnet() understands are accepted, anything else
|
||||
* would be silently ignored when the restriction is checked.
|
||||
*
|
||||
* - Address: 192.168.1.1 or 2001:db8::1
|
||||
* - Subnet: 192.168.1.0/24 or 2001:db8::/32
|
||||
* - Range in the last group: 192.168.1.1-255 or 2001:db8::1-ff
|
||||
*
|
||||
* @param string $entry A single IP address entry.
|
||||
*
|
||||
* @return bool True if the entry is valid.
|
||||
*/
|
||||
public static function is_valid_ipaddress_entry(string $entry): bool {
|
||||
if (strpos($entry, '/') !== false) {
|
||||
$valid = self::is_valid_subnet($entry);
|
||||
} else if (strpos($entry, '-') !== false) {
|
||||
$valid = self::is_valid_range($entry);
|
||||
} else {
|
||||
// An address ending in ":" is treated as an incomplete prefix by address_in_subnet(), which fails to parse it.
|
||||
$valid = self::is_ipv4($entry) || (self::is_ipv6($entry) && substr($entry, -1) !== ':');
|
||||
}
|
||||
|
||||
// Safety net: the entry must match its own start address, so address_in_subnet() is able to parse it.
|
||||
return $valid && address_in_subnet(preg_split('/[\/-]/', $entry)[0], $entry, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value is a valid IPv4 address.
|
||||
*
|
||||
* @param string $value The value to check.
|
||||
*
|
||||
* @return bool True if the value is a valid IPv4 address.
|
||||
*/
|
||||
private static function is_ipv4(string $value): bool {
|
||||
return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value is a valid IPv6 address that cleanremoteaddr() can parse.
|
||||
*
|
||||
* cleanremoteaddr() doesn't handle embedded IPv4 notation reliably and only expands "::"
|
||||
* when it replaces at least two groups, or three at the start or end of the address.
|
||||
*
|
||||
* @param string $value The value to check.
|
||||
*
|
||||
* @return bool True if the value is a valid IPv6 address.
|
||||
*/
|
||||
private static function is_ipv6(string $value): bool {
|
||||
if (strpos($value, '.') !== false || filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strpos($value, '::') === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$groups = count(array_filter(explode(':', $value), 'strlen'));
|
||||
if (strpos($value, '::') === 0 || substr($value, -2) === '::') {
|
||||
return $groups <= 5;
|
||||
}
|
||||
|
||||
return $groups <= 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a subnet in CIDR notation, e.g. 192.168.1.0/24 or 2001:db8::/32.
|
||||
*
|
||||
* @param string $entry The subnet.
|
||||
*
|
||||
* @return bool True if the subnet is valid.
|
||||
*/
|
||||
private static function is_valid_subnet(string $entry): bool {
|
||||
$parts = explode('/', $entry);
|
||||
|
||||
// Moodle's is_number() rejects a mask with leading zeros.
|
||||
if (count($parts) !== 2 || !preg_match('/^(0|[1-9]\d{0,2})$/D', $parts[1])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
[$address, $mask] = $parts;
|
||||
if (self::is_ipv4($address)) {
|
||||
return (int) $mask <= 32;
|
||||
}
|
||||
|
||||
return self::is_ipv6($address) && (int) $mask <= 128;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a range in the last group of the address, e.g. 192.168.1.1-255 or 2001:db8::1-ff.
|
||||
*
|
||||
* The end of the range may not be lower than the start, it would never match.
|
||||
*
|
||||
* @param string $entry The range.
|
||||
*
|
||||
* @return bool True if the range is valid.
|
||||
*/
|
||||
private static function is_valid_range(string $entry): bool {
|
||||
$parts = explode('-', $entry);
|
||||
if (count($parts) !== 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
[$start, $end] = $parts;
|
||||
if (self::is_ipv4($start)) {
|
||||
$startgroup = (int) substr(strrchr($start, '.'), 1);
|
||||
|
||||
return preg_match('/^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/D', $end) === 1 && (int) $end >= $startgroup;
|
||||
}
|
||||
|
||||
if (self::is_ipv6($start)) {
|
||||
$startgroup = hexdec(substr(strrchr($start, ':'), 1) ?: '0');
|
||||
|
||||
return preg_match('/^[0-9a-f]{1,4}$/iD', $end) === 1 && hexdec($end) >= $startgroup;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -7,12 +7,12 @@
|
||||
<TABLE NAME="availability_ipaddress_pre" COMMENT="Stores predefined IP address ranges for availability_ipaddress">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" COMMENT="Display name for the IP range"/>
|
||||
<FIELD NAME="ipaddresses" TYPE="text" NOTNULL="true" COMMENT="Comma-separated list of IP addresses/ranges"/>
|
||||
<FIELD NAME="description" TYPE="text" NOTNULL="false" COMMENT="Optional description of this IP range"/>
|
||||
<FIELD NAME="enabled" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" COMMENT="Whether this range is available for selection"/>
|
||||
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0"/>
|
||||
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0"/>
|
||||
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" COMMENT="Display name for the IP range" SEQUENCE="false"/>
|
||||
<FIELD NAME="ipaddresses" TYPE="text" NOTNULL="true" COMMENT="Comma-separated list of IP addresses/ranges" SEQUENCE="false"/>
|
||||
<FIELD NAME="description" TYPE="text" NOTNULL="false" COMMENT="Optional description of this IP range" SEQUENCE="false"/>
|
||||
<FIELD NAME="enabled" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" COMMENT="Whether this range is available for selection" SEQUENCE="false"/>
|
||||
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
|
||||
@@ -56,11 +56,12 @@ $string['ipaddresses_help'] = 'Enter IP addresses separated by commas. Supports
|
||||
$string['ipaddresses_help_help'] = '<p>Enter one or more IP addresses or ranges, separated by commas.</p>
|
||||
<p><strong>Examples:</strong></p>
|
||||
<ul>
|
||||
<li><strong>Single IP:</strong> <code>192.168.1.1</code></li>
|
||||
<li><strong>IP range:</strong> <code>192.168.1.1-255</code></li>
|
||||
<li><strong>Subnet:</strong> <code>192.168.1.0/24</code></li>
|
||||
<li><strong>Single IP:</strong> <code>192.168.1.1</code> or <code>2001:db8::1</code></li>
|
||||
<li><strong>IP range:</strong> <code>192.168.1.1-255</code> or <code>2001:db8::1-ff</code></li>
|
||||
<li><strong>Subnet:</strong> <code>192.168.1.0/24</code> or <code>2001:db8::/32</code></li>
|
||||
<li><strong>Multiple:</strong> <code>192.168.1.1, 10.0.0.0/8, 172.16.0.1-255</code></li>
|
||||
</ul>';
|
||||
</ul>
|
||||
<p>A range only changes the last part of the address. Use a subnet for an IPv6 network prefix, e.g. <code>2001:db8::/32</code> instead of <code>2001:db8::</code>.</p>';
|
||||
$string['enabled'] = 'Enabled';
|
||||
$string['sortorder'] = 'Sort order';
|
||||
$string['existing_ranges'] = 'Existing IP ranges';
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace availability_ipaddress;
|
||||
|
||||
/**
|
||||
* Unit tests for the IP address validation in the helper class.
|
||||
*
|
||||
* @package availability_ipaddress
|
||||
* @copyright 2026 LdesignMedia.nl - Luuk Verhoeven
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \availability_ipaddress\helper
|
||||
*/
|
||||
final class helper_test extends \basic_testcase {
|
||||
/**
|
||||
* Data provider for test_is_valid_ipaddress_list.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function ipaddress_list_provider(): array {
|
||||
return [
|
||||
'IPv4 address' => ['192.168.1.1', true],
|
||||
'IPv4 subnet' => ['192.168.1.0/24', true],
|
||||
'IPv4 subnet all addresses' => ['0.0.0.0/0', true],
|
||||
'IPv4 range' => ['192.168.1.10-20', true],
|
||||
'IPv4 range single address' => ['192.168.1.5-5', true],
|
||||
'IPv6 address' => ['2001:db8::1', true],
|
||||
'IPv6 loopback' => ['::1', true],
|
||||
'IPv6 full address' => ['2001:db8:0:0:0:0:0:1', true],
|
||||
'IPv6 compressed in the middle' => ['1:2:3::4:5:6', true],
|
||||
'IPv6 compressed at the start' => ['::1:2:3:4:5', true],
|
||||
'IPv6 subnet' => ['2001:db8::/32', true],
|
||||
'IPv6 subnet all addresses' => ['::/0', true],
|
||||
'IPv6 range' => ['2001:db8::1-ff', true],
|
||||
'IPv6 range from compressed start' => ['2001:db8::-ffff', true],
|
||||
'List' => ['192.168.1.1,10.0.0.0/8,2001:db8::/32', true],
|
||||
'List with spaces' => [' 192.168.1.1 , 10.0.0.1 ', true],
|
||||
'IPv6 with trailing text' => ['2001:db8::1garbage', false],
|
||||
'IPv6 with zone id' => ['fe80::1%eth0', false],
|
||||
'IPv6 subnet mask too large' => ['2001:db8::/129', false],
|
||||
'IPv6 address ending in ::' => ['2001:db8::', false],
|
||||
'IPv6 unspecified address' => ['::', false],
|
||||
'IPv6 :: replacing one group' => ['1::2:3:4:5:6:7', false],
|
||||
'IPv6 :: at the start replacing two groups' => ['::1:2:3:4:5:6', false],
|
||||
'IPv6 subnet :: at the end replacing two groups' => ['2001:db8:1:2:3:4::/112', false],
|
||||
'IPv6 with embedded IPv4' => ['::ffff:192.168.1.1', false],
|
||||
'IPv6 subnet with embedded IPv4' => ['::ffff:192.168.1.0/120', false],
|
||||
'IPv4 subnet with trailing text' => ['192.168.1.0/24garbage', false],
|
||||
'IPv4 subnet mask too large' => ['192.168.1.0/33', false],
|
||||
'IPv4 subnet mask with leading zero' => ['192.168.1.0/024', false],
|
||||
'IPv4 subnet negative mask' => ['192.168.1.0/-5', false],
|
||||
'IPv4 subnet exponent mask' => ['192.168.1.0/1e3', false],
|
||||
'IPv4 subnet trailing newline' => ["192.168.1.0/24\n", true],
|
||||
'IPv4 with non-breaking space' => ["192.168.1.1\u{a0}", false],
|
||||
'IPv4 subnet double mask' => ['192.168.1.0/24/8', false],
|
||||
'IPv4 range with text' => ['192.168.1.1-garbage', false],
|
||||
'IPv4 range end too large' => ['192.168.1.1-256', false],
|
||||
'IPv4 range end below start' => ['192.168.1.20-10', false],
|
||||
'IPv4 range double dash' => ['192.168.1.1-2-3', false],
|
||||
'IPv6 range end below start' => ['2001:db8::ff-1', false],
|
||||
'Invalid octet' => ['999.1.1.1', false],
|
||||
'Leading zero' => ['192.168.01.1', false],
|
||||
'Text' => ['abc', false],
|
||||
'Text with subnet' => ['hello/gm', false],
|
||||
'Empty' => ['', false],
|
||||
'Zero' => ['0', false],
|
||||
'Trailing comma' => ['192.168.1.1,', false],
|
||||
'List with one invalid entry' => ['192.168.1.1, 2001:db8::1garbage', false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test validation of a comma separated list of IP addresses.
|
||||
*
|
||||
* @dataProvider ipaddress_list_provider
|
||||
*
|
||||
* @param string $ipaddresses
|
||||
* @param bool $expected
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_is_valid_ipaddress_list(string $ipaddresses, bool $expected): void {
|
||||
$this->assertSame($expected, helper::is_valid_ipaddress_list($ipaddresses));
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -27,8 +27,8 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->component = 'availability_ipaddress';
|
||||
$plugin->version = 2026031800;
|
||||
$plugin->release = '5.1.1';
|
||||
$plugin->version = 2026101500;
|
||||
$plugin->release = '5.3.2';
|
||||
$plugin->requires = 2016120500;
|
||||
$plugin->maturity = MATURITY_STABLE;
|
||||
$plugin->supported = [400, 501];
|
||||
$plugin->supported = [400, 503];
|
||||
+132
-51
@@ -15,11 +15,12 @@ YUI.add('moodle-availability_ipaddress-form', function (Y, NAME) {
|
||||
*/
|
||||
M.availability_ipaddress = M.availability_ipaddress || {};
|
||||
|
||||
// A single IPv4 octet (0-255) without leading zeros.
|
||||
M.availability_ipaddress.octet = '(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)';
|
||||
|
||||
// MIT https://github.com/sindresorhus/ip-regex
|
||||
// Advanced ip-address regex for validating.
|
||||
M.availability_ipaddress.v4 = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]' +
|
||||
'[0-9]|[1-9][0-9]|[0-9])){3}';
|
||||
M.availability_ipaddress.v6 = "^((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]" +
|
||||
// IPv6 address, without anchors so it can be combined.
|
||||
M.availability_ipaddress.v6 = "((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]" +
|
||||
"\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){5}" +
|
||||
"(?::(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4})" +
|
||||
"{1,2}|:)|(?:[a-fA-F\\d]{1,4}:){4}(?:(:[a-fA-F\\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]" +
|
||||
@@ -29,7 +30,7 @@ M.availability_ipaddress.v6 = "^((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|
|
||||
"\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,5}|:)|(?:[a-fA-F\\d]{1,4}:){1}(?:(:[a-fA-F\\d]{1,4}){0,4}:(?:25[0-5]" +
|
||||
"|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,6}|:)|(?::" +
|
||||
"((?::[a-fA-F\\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}" +
|
||||
"|(?::[a-fA-F\\d]{1,4}){1,7}|:)))(%[0-9a-zA-Z]{1,})?";
|
||||
"|(?::[a-fA-F\\d]{1,4}){1,7}|:)))";
|
||||
|
||||
/**
|
||||
* @class M.availability_ipaddress.form
|
||||
@@ -163,60 +164,141 @@ M.availability_ipaddress.form.getNode = function(json) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate ipaddresses
|
||||
* Check if a value is a valid IPv4 address.
|
||||
*
|
||||
* @param {string[]} ipaddresses
|
||||
* @param {string} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.isIpv4 = function(value) {
|
||||
'use strict';
|
||||
var octet = M.availability_ipaddress.octet;
|
||||
return new RegExp('^' + octet + '(?:\\.' + octet + '){3}$').test(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a value is a valid IPv6 address that Moodle's cleanremoteaddr() can parse.
|
||||
*
|
||||
* cleanremoteaddr() doesn't handle embedded IPv4 notation reliably and only expands "::"
|
||||
* when it replaces at least two groups, or three at the start or end of the address.
|
||||
*
|
||||
* @param {string} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.isIpv6 = function(value) {
|
||||
'use strict';
|
||||
var groups;
|
||||
|
||||
if (value.indexOf('.') !== -1 || !new RegExp('^' + M.availability_ipaddress.v6 + '$').test(value)) {
|
||||
return false;
|
||||
}
|
||||
if (value.indexOf('::') === -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
groups = value.split(':').filter(function(group) {
|
||||
return group !== '';
|
||||
}).length;
|
||||
if (value.indexOf('::') === 0 || value.slice(-2) === '::') {
|
||||
return groups <= 5;
|
||||
}
|
||||
return groups <= 6;
|
||||
};
|
||||
|
||||
/**
|
||||
* Trim the same characters as PHP's trim(), which address_in_subnet() uses.
|
||||
*
|
||||
* @param {string} value
|
||||
* @returns {string}
|
||||
*/
|
||||
M.availability_ipaddress.trim = function(value) {
|
||||
'use strict';
|
||||
var chars = ' \t\n\r\x00\x0B',
|
||||
start = 0,
|
||||
end = value.length;
|
||||
|
||||
while (start < end && chars.indexOf(value.charAt(start)) !== -1) {
|
||||
start++;
|
||||
}
|
||||
while (end > start && chars.indexOf(value.charAt(end - 1)) !== -1) {
|
||||
end--;
|
||||
}
|
||||
return value.substring(start, end);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate a single entry. Only formats that Moodle's address_in_subnet() understands are
|
||||
* accepted, anything else would be silently ignored when the restriction is checked.
|
||||
*
|
||||
* - Address: 192.168.1.1 or 2001:db8::1
|
||||
* - Subnet: 192.168.1.0/24 or 2001:db8::/32
|
||||
* - Range in the last group: 192.168.1.1-255 or 2001:db8::1-ff
|
||||
*
|
||||
* @param {string} entry
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.validateEntry = function(entry) {
|
||||
'use strict';
|
||||
var parts, mask, lastgroup;
|
||||
|
||||
if (entry.indexOf('/') !== -1) {
|
||||
parts = entry.split('/');
|
||||
// Moodle's is_number() rejects a mask with leading zeros.
|
||||
if (parts.length !== 2 || !/^(0|[1-9]\d{0,2})$/.test(parts[1])) {
|
||||
return false;
|
||||
}
|
||||
mask = parseInt(parts[1], 10);
|
||||
if (M.availability_ipaddress.isIpv4(parts[0])) {
|
||||
return mask <= 32;
|
||||
}
|
||||
return M.availability_ipaddress.isIpv6(parts[0]) && mask <= 128;
|
||||
}
|
||||
|
||||
if (entry.indexOf('-') !== -1) {
|
||||
parts = entry.split('-');
|
||||
if (parts.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The end of the range may not be lower than the start, it would never match.
|
||||
if (M.availability_ipaddress.isIpv4(parts[0])) {
|
||||
return new RegExp('^' + M.availability_ipaddress.octet + '$').test(parts[1]) &&
|
||||
parseInt(parts[1], 10) >= parseInt(parts[0].split('.')[3], 10);
|
||||
}
|
||||
if (M.availability_ipaddress.isIpv6(parts[0])) {
|
||||
lastgroup = parts[0].split(':').pop() || '0';
|
||||
return /^[a-fA-F\d]{1,4}$/.test(parts[1]) && parseInt(parts[1], 16) >= parseInt(lastgroup, 16);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// An address ending in ":" is treated as an incomplete prefix by address_in_subnet(), which fails to parse it.
|
||||
return M.availability_ipaddress.isIpv4(entry) || (M.availability_ipaddress.isIpv6(entry) && entry.slice(-1) !== ':');
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate a comma separated list of ipaddresses.
|
||||
*
|
||||
* @param {string} ipaddresses
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
||||
'use strict';
|
||||
Y.log(ipaddresses);
|
||||
// Return true for empty string - it's valid to have no custom IPs
|
||||
if (!ipaddresses || ipaddresses.trim() === '') {
|
||||
var entries, i;
|
||||
|
||||
// Return true for empty string - it's valid to have no custom IPs.
|
||||
if (!ipaddresses || M.availability_ipaddress.trim(ipaddresses) === '') {
|
||||
return true;
|
||||
}
|
||||
ipaddresses = ipaddresses.split(',');
|
||||
for (var i in ipaddresses) {
|
||||
|
||||
// Test normal ip format.
|
||||
// Strict ipv4 check.
|
||||
if (new RegExp(/^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/gm)
|
||||
.test(ipaddresses[i])) {
|
||||
Y.log('Correct ipv4');
|
||||
continue;
|
||||
}
|
||||
|
||||
var ipv4Regex = new RegExp(
|
||||
'^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)' +
|
||||
'(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}-' +
|
||||
'(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)$',
|
||||
'gm'
|
||||
);
|
||||
|
||||
if (ipv4Regex.test(ipaddresses[i])) {
|
||||
Y.log('Correct ipv4 range.');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (new RegExp(M.availability_ipaddress.v6)
|
||||
.test(ipaddresses[i])) {
|
||||
Y.log('Correct ipv6');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Test subnet with a regex.
|
||||
if (new RegExp("^(?:".concat(M.availability_ipaddress.v4 + "\\/(3[0-2]|[12]?[0-9])|(1\\*)", ")|(?:")
|
||||
.concat(M.availability_ipaddress.v6 + "\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])", ")?\\/gm"))
|
||||
.test(ipaddresses[i])) {
|
||||
Y.log('Correct subnet');
|
||||
continue;
|
||||
}
|
||||
|
||||
Y.log('Incorrect ip');
|
||||
entries = ipaddresses.split(',');
|
||||
for (i = 0; i < entries.length; i++) {
|
||||
// Whitespace around an entry is allowed, address_in_subnet() trims it as well.
|
||||
if (!M.availability_ipaddress.validateEntry(M.availability_ipaddress.trim(entries[i]))) {
|
||||
Y.log('Incorrect ip-address: ' + entries[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Y.log('Valid ipaddresses');
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -259,9 +341,8 @@ M.availability_ipaddress.form.fillErrors = function(errors, node) {
|
||||
var value = {};
|
||||
this.fillValue(value, node);
|
||||
|
||||
// Basic ipaddresses checks - only validate if not empty.
|
||||
if (value.ipaddresses && value.ipaddresses.trim() !== '' &&
|
||||
M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
||||
// Basic ipaddresses checks - an empty value is valid.
|
||||
if (M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
||||
errors.push('availability_ipaddress:error_ipaddress');
|
||||
}
|
||||
};
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-availability_ipaddress-form",function(r,d){M.availability_ipaddress=M.availability_ipaddress||{},M.availability_ipaddress.v4="(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])){3}",M.availability_ipaddress.v6="^((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,2}|:)|(?:[a-fA-F\\d]{1,4}:){4}(?:(:[a-fA-F\\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,3}|:)|(?:[a-fA-F\\d]{1,4}:){3}(?:(:[a-fA-F\\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,4}|:)|(?:[a-fA-F\\d]{1,4}:){2}(?:(:[a-fA-F\\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,5}|:)|(?:[a-fA-F\\d]{1,4}:){1}(?:(:[a-fA-F\\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,6}|:)|(?::((?::[a-fA-F\\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,7}|:)))(%[0-9a-zA-Z]{1,})?",M.availability_ipaddress.form=r.Object(M.core_availability.plugin),M.availability_ipaddress.form.initInner=function(d){"use strict";this.predefinedRanges=d||[]},M.availability_ipaddress.form.getValue=function(d,a){"use strict";d=a.one("input[name="+d+"]").get("value");return M.availability_ipaddress.validateIpaddress(d),d},M.availability_ipaddress.form.getNode=function(d){"use strict";var a,e,i,s,t="ipaddresses"+M.availability_ipaddress.form.instId,l="predefined"+M.availability_ipaddress.form.instId;if(M.availability_ipaddress.form.instId+=1,a='<div class="availability-ipaddress-container">',this.predefinedRanges&&0<this.predefinedRanges.length){for(a=(a+='<div class="availability-group">')+('<label><span class="p-r-1">'+M.util.get_string("use_predefined","availability_ipaddress")+"</span></label>")+('<select name="predefined_ranges" id="'+l+'" multiple="multiple" class="form-control" style="min-height: 100px;">'),e=0;e<this.predefinedRanges.length;e++)a=(a+='<option value="'+(i=this.predefinedRanges[e]).id+'" data-ipaddresses="'+r.Escape.html(i.ipaddresses)+'">')+r.Escape.html(i.name)+"</option>";a=(a=(a+="</select>")+"</div>"+'<div class="availability-group" style="margin-top: 10px;">')+('<label for="'+t+'"><span class="p-r-1">'+M.util.get_string("custom_ipaddress","availability_ipaddress")+"</span></label>")}else a=(a+='<div class="availability-group">')+'<label for="'+t+'"><span class="p-r-1">'+M.util.get_string("title","availability_ipaddress")+"</span></label>";return l=r.Node.create("<div>"+(a=(a+='<input type="text" placeholder="192.168.178.1,231.54.211.0/20,231.3.56.211" name="ipaddresses" id="'+t+'" class="form-control">')+"</div>"+"</div>")+"</div>"),d.ipaddresses!==undefined&&l.one("input[name=ipaddresses]").set("value",d.ipaddresses),d.predefined_ranges!==undefined&&this.predefinedRanges&&0<this.predefinedRanges.length&&(s=l.one("select[name=predefined_ranges]"))&&d.predefined_ranges.forEach(function(d){d=s.one('option[value="'+d+'"]');d&&d.set("selected",!0)}),M.availability_ipaddress.form.addedEvents||(M.availability_ipaddress.form.addedEvents=!0,(t=r.one(".availability-field")).delegate("valuechange",function(){M.core_availability.form.update()},".availability_ipaddress input[name=ipaddresses]"),t.delegate("change",function(){M.core_availability.form.update()},".availability_ipaddress select[name=predefined_ranges]")),l},M.availability_ipaddress.validateIpaddress=function(d){"use strict";if(!d||""===d.trim())return!0;for(var a in d=d.split(","))if(!(new RegExp(/^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/gm).test(d[a])||new RegExp("^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}-(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)$","gm").test(d[a])||new RegExp(M.availability_ipaddress.v6).test(d[a])||new RegExp("^(?:".concat(M.availability_ipaddress.v4+"\\/(3[0-2]|[12]?[0-9])|(1\\*)",")|(?:").concat(M.availability_ipaddress.v6+"\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])",")?\\/gm")).test(d[a])))return!1;return!0},M.availability_ipaddress.form.fillValue=function(d,a){var e;d.ipaddresses=this.getValue("ipaddresses",a),(a=a.one("select[name=predefined_ranges]"))&&(e=[],a.get("options").each(function(d){d.get("selected")&&e.push(parseInt(d.get("value")))}),0<e.length&&(d.predefined_ranges=e))},M.availability_ipaddress.form.fillErrors=function(d,a){"use strict";var e={};this.fillValue(e,a),e.ipaddresses&&""!==e.ipaddresses.trim()&&!1===M.availabiLine truncated
|
||||
YUI.add("moodle-availability_ipaddress-form",function(r,i){M.availability_ipaddress=M.availability_ipaddress||{},M.availability_ipaddress.octet="(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)",M.availability_ipaddress.v6="((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,2}|:)|(?:[a-fA-F\\d]{1,4}:){4}(?:(:[a-fA-F\\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,3}|:)|(?:[a-fA-F\\d]{1,4}:){3}(?:(:[a-fA-F\\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,4}|:)|(?:[a-fA-F\\d]{1,4}:){2}(?:(:[a-fA-F\\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,5}|:)|(?:[a-fA-F\\d]{1,4}:){1}(?:(:[a-fA-F\\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,6}|:)|(?::((?::[a-fA-F\\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,7}|:)))",M.availability_ipaddress.form=r.Object(M.core_availability.plugin),M.availability_ipaddress.form.initInner=function(i){"use strict";this.predefinedRanges=i||[]},M.availability_ipaddress.form.getValue=function(i,a){"use strict";i=a.one("input[name="+i+"]").get("value");return M.availability_ipaddress.validateIpaddress(i),i},M.availability_ipaddress.form.getNode=function(i){"use strict";var a,e,d,s,t="ipaddresses"+M.availability_ipaddress.form.instId,l="predefined"+M.availability_ipaddress.form.instId;if(M.availability_ipaddress.form.instId+=1,a='<div class="availability-ipaddress-container">',this.predefinedRanges&&0<this.predefinedRanges.length){for(a=(a+='<div class="availability-group">')+('<label><span class="p-r-1">'+M.util.get_string("use_predefined","availability_ipaddress")+"</span></label>")+('<select name="predefined_ranges" id="'+l+'" multiple="multiple" class="form-control" style="min-height: 100px;">'),e=0;e<this.predefinedRanges.length;e++)a=(a+='<option value="'+(d=this.predefinedRanges[e]).id+'" data-ipaddresses="'+r.Escape.html(d.ipaddresses)+'">')+r.Escape.html(d.name)+"</option>";a=(a=(a+="</select>")+"</div>"+'<div class="availability-group" style="margin-top: 10px;">')+('<label for="'+t+'"><span class="p-r-1">'+M.util.get_string("custom_ipaddress","availability_ipaddress")+"</span></label>")}else a=(a+='<div class="availability-group">')+'<label for="'+t+'"><span class="p-r-1">'+M.util.get_string("title","availability_ipaddress")+"</span></label>";return l=r.Node.create("<div>"+(a=(a+='<input type="text" placeholder="192.168.178.1,231.54.211.0/20,231.3.56.211" name="ipaddresses" id="'+t+'" class="form-control">')+"</div>"+"</div>")+"</div>"),i.ipaddresses!==undefined&&l.one("input[name=ipaddresses]").set("value",i.ipaddresses),i.predefined_ranges!==undefined&&this.predefinedRanges&&0<this.predefinedRanges.length&&(s=l.one("select[name=predefined_ranges]"))&&i.predefined_ranges.forEach(function(i){i=s.one('option[value="'+i+'"]');i&&i.set("selected",!0)}),M.availability_ipaddress.form.addedEvents||(M.availability_ipaddress.form.addedEvents=!0,(t=r.one(".availability-field")).delegate("valuechange",function(){M.core_availability.form.update()},".availability_ipaddress input[name=ipaddresses]"),t.delegate("change",function(){M.core_availability.form.update()},".availability_ipaddress select[name=predefined_ranges]")),l},M.availability_ipaddress.isIpv4=function(i){"use strict";var a=M.availability_ipaddress.octet;return new RegExp("^"+a+"(?:\\."+a+"){3}$").test(i)},M.availability_ipaddress.isIpv6=function(i){"use strict";var a;return!(-1!==i.indexOf(".")||!new RegExp("^"+M.availability_ipaddress.v6+"$").test(i))&&(-1===i.indexOf("::")||(a=i.split(":").filter(function(i){return""!==i}).length,0===i.indexOf("::")||"::"===i.slice(-2)?a<=5:a<=6))},M.availability_ipaddress.trim=function(i){"use strict";for(var a=" \t\n\r\0\x0B",e=0,d=i.length;e<d&&-1!==a.indexOf(i.charAt(e));)e++;for(;e<d&&-1!==a.indexOf(i.charAt(d-1));)d--;return i.substring(e,d)},M.availability_ipaddress.validateEntry=function(i){"use strict";var a,e;return-1!==i.indexOf("/")?!(2!==(a=i.split("/")).length||!/^(0|[1-9]\d{0,2})$/.test(a[1]))&&(e=parseInt(a[1],10),M.availability_ipaddress.isIpv4(a[0])?e<=32:M.availability_ipaddress.isIpv6(a[0])&&e<=128):-1!==i.indexOf("-")?2===(a=i.split("-")).length&&(M.availability_ipaddress.isIpv4(a[0])?new RegExp("^"+M.availability_ipaddress.octet+"$").test(a[1])&&parseInt(a[1],10)>=parseInt(a[0].split(".")[3],10):!!M.availability_ipaddress.isIpv6(a[0])&&(e=a[0].split(":").pop()||"0",/^[a-fA-F\d]{1,4}$/Line truncated
|
||||
+131
-44
@@ -15,11 +15,12 @@ YUI.add('moodle-availability_ipaddress-form', function (Y, NAME) {
|
||||
*/
|
||||
M.availability_ipaddress = M.availability_ipaddress || {};
|
||||
|
||||
// A single IPv4 octet (0-255) without leading zeros.
|
||||
M.availability_ipaddress.octet = '(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)';
|
||||
|
||||
// MIT https://github.com/sindresorhus/ip-regex
|
||||
// Advanced ip-address regex for validating.
|
||||
M.availability_ipaddress.v4 = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]' +
|
||||
'[0-9]|[1-9][0-9]|[0-9])){3}';
|
||||
M.availability_ipaddress.v6 = "^((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]" +
|
||||
// IPv6 address, without anchors so it can be combined.
|
||||
M.availability_ipaddress.v6 = "((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]" +
|
||||
"\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){5}" +
|
||||
"(?::(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4})" +
|
||||
"{1,2}|:)|(?:[a-fA-F\\d]{1,4}:){4}(?:(:[a-fA-F\\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]" +
|
||||
@@ -29,7 +30,7 @@ M.availability_ipaddress.v6 = "^((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|
|
||||
"\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,5}|:)|(?:[a-fA-F\\d]{1,4}:){1}(?:(:[a-fA-F\\d]{1,4}){0,4}:(?:25[0-5]" +
|
||||
"|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,6}|:)|(?::" +
|
||||
"((?::[a-fA-F\\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}" +
|
||||
"|(?::[a-fA-F\\d]{1,4}){1,7}|:)))(%[0-9a-zA-Z]{1,})?";
|
||||
"|(?::[a-fA-F\\d]{1,4}){1,7}|:)))";
|
||||
|
||||
/**
|
||||
* @class M.availability_ipaddress.form
|
||||
@@ -160,52 +161,139 @@ M.availability_ipaddress.form.getNode = function(json) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate ipaddresses
|
||||
* Check if a value is a valid IPv4 address.
|
||||
*
|
||||
* @param {string[]} ipaddresses
|
||||
* @param {string} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.isIpv4 = function(value) {
|
||||
'use strict';
|
||||
var octet = M.availability_ipaddress.octet;
|
||||
return new RegExp('^' + octet + '(?:\\.' + octet + '){3}$').test(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a value is a valid IPv6 address that Moodle's cleanremoteaddr() can parse.
|
||||
*
|
||||
* cleanremoteaddr() doesn't handle embedded IPv4 notation reliably and only expands "::"
|
||||
* when it replaces at least two groups, or three at the start or end of the address.
|
||||
*
|
||||
* @param {string} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.isIpv6 = function(value) {
|
||||
'use strict';
|
||||
var groups;
|
||||
|
||||
if (value.indexOf('.') !== -1 || !new RegExp('^' + M.availability_ipaddress.v6 + '$').test(value)) {
|
||||
return false;
|
||||
}
|
||||
if (value.indexOf('::') === -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
groups = value.split(':').filter(function(group) {
|
||||
return group !== '';
|
||||
}).length;
|
||||
if (value.indexOf('::') === 0 || value.slice(-2) === '::') {
|
||||
return groups <= 5;
|
||||
}
|
||||
return groups <= 6;
|
||||
};
|
||||
|
||||
/**
|
||||
* Trim the same characters as PHP's trim(), which address_in_subnet() uses.
|
||||
*
|
||||
* @param {string} value
|
||||
* @returns {string}
|
||||
*/
|
||||
M.availability_ipaddress.trim = function(value) {
|
||||
'use strict';
|
||||
var chars = ' \t\n\r\x00\x0B',
|
||||
start = 0,
|
||||
end = value.length;
|
||||
|
||||
while (start < end && chars.indexOf(value.charAt(start)) !== -1) {
|
||||
start++;
|
||||
}
|
||||
while (end > start && chars.indexOf(value.charAt(end - 1)) !== -1) {
|
||||
end--;
|
||||
}
|
||||
return value.substring(start, end);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate a single entry. Only formats that Moodle's address_in_subnet() understands are
|
||||
* accepted, anything else would be silently ignored when the restriction is checked.
|
||||
*
|
||||
* - Address: 192.168.1.1 or 2001:db8::1
|
||||
* - Subnet: 192.168.1.0/24 or 2001:db8::/32
|
||||
* - Range in the last group: 192.168.1.1-255 or 2001:db8::1-ff
|
||||
*
|
||||
* @param {string} entry
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.validateEntry = function(entry) {
|
||||
'use strict';
|
||||
var parts, mask, lastgroup;
|
||||
|
||||
if (entry.indexOf('/') !== -1) {
|
||||
parts = entry.split('/');
|
||||
// Moodle's is_number() rejects a mask with leading zeros.
|
||||
if (parts.length !== 2 || !/^(0|[1-9]\d{0,2})$/.test(parts[1])) {
|
||||
return false;
|
||||
}
|
||||
mask = parseInt(parts[1], 10);
|
||||
if (M.availability_ipaddress.isIpv4(parts[0])) {
|
||||
return mask <= 32;
|
||||
}
|
||||
return M.availability_ipaddress.isIpv6(parts[0]) && mask <= 128;
|
||||
}
|
||||
|
||||
if (entry.indexOf('-') !== -1) {
|
||||
parts = entry.split('-');
|
||||
if (parts.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The end of the range may not be lower than the start, it would never match.
|
||||
if (M.availability_ipaddress.isIpv4(parts[0])) {
|
||||
return new RegExp('^' + M.availability_ipaddress.octet + '$').test(parts[1]) &&
|
||||
parseInt(parts[1], 10) >= parseInt(parts[0].split('.')[3], 10);
|
||||
}
|
||||
if (M.availability_ipaddress.isIpv6(parts[0])) {
|
||||
lastgroup = parts[0].split(':').pop() || '0';
|
||||
return /^[a-fA-F\d]{1,4}$/.test(parts[1]) && parseInt(parts[1], 16) >= parseInt(lastgroup, 16);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// An address ending in ":" is treated as an incomplete prefix by address_in_subnet(), which fails to parse it.
|
||||
return M.availability_ipaddress.isIpv4(entry) || (M.availability_ipaddress.isIpv6(entry) && entry.slice(-1) !== ':');
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate a comma separated list of ipaddresses.
|
||||
*
|
||||
* @param {string} ipaddresses
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
||||
'use strict';
|
||||
// Return true for empty string - it's valid to have no custom IPs
|
||||
if (!ipaddresses || ipaddresses.trim() === '') {
|
||||
var entries, i;
|
||||
|
||||
// Return true for empty string - it's valid to have no custom IPs.
|
||||
if (!ipaddresses || M.availability_ipaddress.trim(ipaddresses) === '') {
|
||||
return true;
|
||||
}
|
||||
ipaddresses = ipaddresses.split(',');
|
||||
for (var i in ipaddresses) {
|
||||
|
||||
// Test normal ip format.
|
||||
// Strict ipv4 check.
|
||||
if (new RegExp(/^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/gm)
|
||||
.test(ipaddresses[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var ipv4Regex = new RegExp(
|
||||
'^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)' +
|
||||
'(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}-' +
|
||||
'(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)$',
|
||||
'gm'
|
||||
);
|
||||
|
||||
if (ipv4Regex.test(ipaddresses[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (new RegExp(M.availability_ipaddress.v6)
|
||||
.test(ipaddresses[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Test subnet with a regex.
|
||||
if (new RegExp("^(?:".concat(M.availability_ipaddress.v4 + "\\/(3[0-2]|[12]?[0-9])|(1\\*)", ")|(?:")
|
||||
.concat(M.availability_ipaddress.v6 + "\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])", ")?\\/gm"))
|
||||
.test(ipaddresses[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entries = ipaddresses.split(',');
|
||||
for (i = 0; i < entries.length; i++) {
|
||||
// Whitespace around an entry is allowed, address_in_subnet() trims it as well.
|
||||
if (!M.availability_ipaddress.validateEntry(M.availability_ipaddress.trim(entries[i]))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -249,9 +337,8 @@ M.availability_ipaddress.form.fillErrors = function(errors, node) {
|
||||
var value = {};
|
||||
this.fillValue(value, node);
|
||||
|
||||
// Basic ipaddresses checks - only validate if not empty.
|
||||
if (value.ipaddresses && value.ipaddresses.trim() !== '' &&
|
||||
M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
||||
// Basic ipaddresses checks - an empty value is valid.
|
||||
if (M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
||||
errors.push('availability_ipaddress:error_ipaddress');
|
||||
}
|
||||
};
|
||||
|
||||
Vendored
+132
-51
@@ -13,11 +13,12 @@
|
||||
*/
|
||||
M.availability_ipaddress = M.availability_ipaddress || {};
|
||||
|
||||
// A single IPv4 octet (0-255) without leading zeros.
|
||||
M.availability_ipaddress.octet = '(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)';
|
||||
|
||||
// MIT https://github.com/sindresorhus/ip-regex
|
||||
// Advanced ip-address regex for validating.
|
||||
M.availability_ipaddress.v4 = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]' +
|
||||
'[0-9]|[1-9][0-9]|[0-9])){3}';
|
||||
M.availability_ipaddress.v6 = "^((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]" +
|
||||
// IPv6 address, without anchors so it can be combined.
|
||||
M.availability_ipaddress.v6 = "((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]" +
|
||||
"\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){5}" +
|
||||
"(?::(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4})" +
|
||||
"{1,2}|:)|(?:[a-fA-F\\d]{1,4}:){4}(?:(:[a-fA-F\\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]" +
|
||||
@@ -27,7 +28,7 @@ M.availability_ipaddress.v6 = "^((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|
|
||||
"\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,5}|:)|(?:[a-fA-F\\d]{1,4}:){1}(?:(:[a-fA-F\\d]{1,4}){0,4}:(?:25[0-5]" +
|
||||
"|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,6}|:)|(?::" +
|
||||
"((?::[a-fA-F\\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}" +
|
||||
"|(?::[a-fA-F\\d]{1,4}){1,7}|:)))(%[0-9a-zA-Z]{1,})?";
|
||||
"|(?::[a-fA-F\\d]{1,4}){1,7}|:)))";
|
||||
|
||||
/**
|
||||
* @class M.availability_ipaddress.form
|
||||
@@ -161,60 +162,141 @@ M.availability_ipaddress.form.getNode = function(json) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate ipaddresses
|
||||
* Check if a value is a valid IPv4 address.
|
||||
*
|
||||
* @param {string[]} ipaddresses
|
||||
* @param {string} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.isIpv4 = function(value) {
|
||||
'use strict';
|
||||
var octet = M.availability_ipaddress.octet;
|
||||
return new RegExp('^' + octet + '(?:\\.' + octet + '){3}$').test(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a value is a valid IPv6 address that Moodle's cleanremoteaddr() can parse.
|
||||
*
|
||||
* cleanremoteaddr() doesn't handle embedded IPv4 notation reliably and only expands "::"
|
||||
* when it replaces at least two groups, or three at the start or end of the address.
|
||||
*
|
||||
* @param {string} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.isIpv6 = function(value) {
|
||||
'use strict';
|
||||
var groups;
|
||||
|
||||
if (value.indexOf('.') !== -1 || !new RegExp('^' + M.availability_ipaddress.v6 + '$').test(value)) {
|
||||
return false;
|
||||
}
|
||||
if (value.indexOf('::') === -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
groups = value.split(':').filter(function(group) {
|
||||
return group !== '';
|
||||
}).length;
|
||||
if (value.indexOf('::') === 0 || value.slice(-2) === '::') {
|
||||
return groups <= 5;
|
||||
}
|
||||
return groups <= 6;
|
||||
};
|
||||
|
||||
/**
|
||||
* Trim the same characters as PHP's trim(), which address_in_subnet() uses.
|
||||
*
|
||||
* @param {string} value
|
||||
* @returns {string}
|
||||
*/
|
||||
M.availability_ipaddress.trim = function(value) {
|
||||
'use strict';
|
||||
var chars = ' \t\n\r\x00\x0B',
|
||||
start = 0,
|
||||
end = value.length;
|
||||
|
||||
while (start < end && chars.indexOf(value.charAt(start)) !== -1) {
|
||||
start++;
|
||||
}
|
||||
while (end > start && chars.indexOf(value.charAt(end - 1)) !== -1) {
|
||||
end--;
|
||||
}
|
||||
return value.substring(start, end);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate a single entry. Only formats that Moodle's address_in_subnet() understands are
|
||||
* accepted, anything else would be silently ignored when the restriction is checked.
|
||||
*
|
||||
* - Address: 192.168.1.1 or 2001:db8::1
|
||||
* - Subnet: 192.168.1.0/24 or 2001:db8::/32
|
||||
* - Range in the last group: 192.168.1.1-255 or 2001:db8::1-ff
|
||||
*
|
||||
* @param {string} entry
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.validateEntry = function(entry) {
|
||||
'use strict';
|
||||
var parts, mask, lastgroup;
|
||||
|
||||
if (entry.indexOf('/') !== -1) {
|
||||
parts = entry.split('/');
|
||||
// Moodle's is_number() rejects a mask with leading zeros.
|
||||
if (parts.length !== 2 || !/^(0|[1-9]\d{0,2})$/.test(parts[1])) {
|
||||
return false;
|
||||
}
|
||||
mask = parseInt(parts[1], 10);
|
||||
if (M.availability_ipaddress.isIpv4(parts[0])) {
|
||||
return mask <= 32;
|
||||
}
|
||||
return M.availability_ipaddress.isIpv6(parts[0]) && mask <= 128;
|
||||
}
|
||||
|
||||
if (entry.indexOf('-') !== -1) {
|
||||
parts = entry.split('-');
|
||||
if (parts.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The end of the range may not be lower than the start, it would never match.
|
||||
if (M.availability_ipaddress.isIpv4(parts[0])) {
|
||||
return new RegExp('^' + M.availability_ipaddress.octet + '$').test(parts[1]) &&
|
||||
parseInt(parts[1], 10) >= parseInt(parts[0].split('.')[3], 10);
|
||||
}
|
||||
if (M.availability_ipaddress.isIpv6(parts[0])) {
|
||||
lastgroup = parts[0].split(':').pop() || '0';
|
||||
return /^[a-fA-F\d]{1,4}$/.test(parts[1]) && parseInt(parts[1], 16) >= parseInt(lastgroup, 16);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// An address ending in ":" is treated as an incomplete prefix by address_in_subnet(), which fails to parse it.
|
||||
return M.availability_ipaddress.isIpv4(entry) || (M.availability_ipaddress.isIpv6(entry) && entry.slice(-1) !== ':');
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate a comma separated list of ipaddresses.
|
||||
*
|
||||
* @param {string} ipaddresses
|
||||
* @returns {boolean}
|
||||
*/
|
||||
M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
|
||||
'use strict';
|
||||
Y.log(ipaddresses);
|
||||
// Return true for empty string - it's valid to have no custom IPs
|
||||
if (!ipaddresses || ipaddresses.trim() === '') {
|
||||
var entries, i;
|
||||
|
||||
// Return true for empty string - it's valid to have no custom IPs.
|
||||
if (!ipaddresses || M.availability_ipaddress.trim(ipaddresses) === '') {
|
||||
return true;
|
||||
}
|
||||
ipaddresses = ipaddresses.split(',');
|
||||
for (var i in ipaddresses) {
|
||||
|
||||
// Test normal ip format.
|
||||
// Strict ipv4 check.
|
||||
if (new RegExp(/^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/gm)
|
||||
.test(ipaddresses[i])) {
|
||||
Y.log('Correct ipv4');
|
||||
continue;
|
||||
}
|
||||
|
||||
var ipv4Regex = new RegExp(
|
||||
'^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)' +
|
||||
'(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}-' +
|
||||
'(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)$',
|
||||
'gm'
|
||||
);
|
||||
|
||||
if (ipv4Regex.test(ipaddresses[i])) {
|
||||
Y.log('Correct ipv4 range.');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (new RegExp(M.availability_ipaddress.v6)
|
||||
.test(ipaddresses[i])) {
|
||||
Y.log('Correct ipv6');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Test subnet with a regex.
|
||||
if (new RegExp("^(?:".concat(M.availability_ipaddress.v4 + "\\/(3[0-2]|[12]?[0-9])|(1\\*)", ")|(?:")
|
||||
.concat(M.availability_ipaddress.v6 + "\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])", ")?\\/gm"))
|
||||
.test(ipaddresses[i])) {
|
||||
Y.log('Correct subnet');
|
||||
continue;
|
||||
}
|
||||
|
||||
Y.log('Incorrect ip');
|
||||
entries = ipaddresses.split(',');
|
||||
for (i = 0; i < entries.length; i++) {
|
||||
// Whitespace around an entry is allowed, address_in_subnet() trims it as well.
|
||||
if (!M.availability_ipaddress.validateEntry(M.availability_ipaddress.trim(entries[i]))) {
|
||||
Y.log('Incorrect ip-address: ' + entries[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Y.log('Valid ipaddresses');
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -257,9 +339,8 @@ M.availability_ipaddress.form.fillErrors = function(errors, node) {
|
||||
var value = {};
|
||||
this.fillValue(value, node);
|
||||
|
||||
// Basic ipaddresses checks - only validate if not empty.
|
||||
if (value.ipaddresses && value.ipaddresses.trim() !== '' &&
|
||||
M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
||||
// Basic ipaddresses checks - an empty value is valid.
|
||||
if (M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
|
||||
errors.push('availability_ipaddress:error_ipaddress');
|
||||
}
|
||||
};
|
||||
Reference in new issue
Block a user