Compare commits

..
11 Commits
Author SHA1 Message Date
Luuk Verhoeven 197e5c3b58 Merge pull request #22 from LdesignMedia/2-fix-ip-validation
Fix #2 - Reject IP addresses that Moodle cannot match
2026-10-16 13:18:37 +02:00
Nihaal Shaikh 06a5f9c8c9 Fix #2 - Reject IP addresses that Moodle cannot match
The custom IP field and the predefined ranges form accepted invalid
entries such as 2001:db8::1garbage, 192.168.1.0/24garbage or
192.168.1.0/999. address_in_subnet() silently skips those entries, so a
"must not" restriction with a typo let blocked users in.

Validation now only accepts formats address_in_subnet() can match: a
single address, a subnet (mask 0-32 / 0-128) or a range in the last
group with end >= start. IPv6 forms cleanremoteaddr() cannot parse are
rejected as well. JS and PHP share the same rules; PHP also checks each
entry against address_in_subnet() as a safety net.

- Allow spaces after commas in the restriction field
- Support IPv6 ranges, e.g. 2001:db8::1-ff
- Add PHPUnit tests for helper::is_valid_ipaddress_list()
- Bump version to 2026101500 (5.3.2)
2026-10-15 09:04:58 +02:00
Luuk Verhoeven 150067f43d Update CHANGELOG.md 2026-10-14 10:51:13 +02:00
Luuk Verhoeven b1f0bc2f09 Update version.php 2026-10-14 10:49:32 +02:00
Nihaal Shaikh 9269cb7f76 Merge pull request #21 from minhduchoang195/#20_fix_failed_unittest
#20 Correct db/install.xml so that the unit test passes successfully.
2026-10-14 10:27:27 +02:00
Hoang, Minh Duc 2210e85ca0 #20 Add the required attribute sequence to each field 2026-10-12 12:56:11 +02:00
Luuk Verhoeven 6b24f4a2b9 Merge pull request #19 from LdesignMedia/124cwve5xb7-upgrade-52-53
124cwve5xb7 upgrade 52 53
2026-09-23 14:08:29 +02:00
Nihaal Shaikh 00941bbc54 Update #124cwve5xb7 - Bump release to 5.3.0 for Moodle 5.3 support 2026-09-23 11:34:38 +02:00
Nihaal Shaikh cf5409707f Update #124cwve5xb7 - Bump release to 5.2.0 for Moodle 5.2 support 2026-09-23 11:01:30 +02:00
Luuk Verhoeven d730ed97fd Merge pull request #17 from LdesignMedia/86c7xvd2d-improve-plugin-visibility
Update #86c7xvd2d - Polish plugin for improved visibility
2026-03-30 11:25:44 +02:00
Hamza Tamyachte 474f91ccbc Update #86c7xvd2d - Polish plugin for improved visibility
- Remove TODO section from README
- Add SECURITY.md
- Update FUNDING.yml to LdesignMedia
2026-03-21 03:04:04 +01:00
14 changed files with 708 additions and 209 deletions

No files matched your search

+1 -1
View File
@@ -1 +1 @@
github: [MFreakNL] github: [LdesignMedia]
+18
View File
@@ -1,5 +1,23 @@
# Changelog # 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) ## 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)) * 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+ * Removed typed properties (PHP 7.4+) to support PHP 7.3+
+2 -3
View File
@@ -21,6 +21,8 @@ activity, making it accessible only to users from specified IP addresses.
![Moodle405](https://img.shields.io/badge/moodle-4.5-F98012.svg?logo=moodle) ![Moodle405](https://img.shields.io/badge/moodle-4.5-F98012.svg?logo=moodle)
![Moodle500](https://img.shields.io/badge/moodle-5.0-F98012.svg?logo=moodle) ![Moodle500](https://img.shields.io/badge/moodle-5.0-F98012.svg?logo=moodle)
![Moodle501](https://img.shields.io/badge/moodle-5.1-F98012.svg?logo=moodle) ![Moodle501](https://img.shields.io/badge/moodle-5.1-F98012.svg?logo=moodle)
![Moodle502](https://img.shields.io/badge/moodle-5.2-F98012.svg?logo=moodle)
![Moodle503](https://img.shields.io/badge/moodle-5.3-F98012.svg?logo=moodle)
![PHP7.3](https://img.shields.io/badge/PHP-7.3-777BB4.svg?logo=php) ![PHP7.3](https://img.shields.io/badge/PHP-7.3-777BB4.svg?logo=php)
![PHP7.4](https://img.shields.io/badge/PHP-7.4-777BB4.svg?logo=php) ![PHP7.4](https://img.shields.io/badge/PHP-7.4-777BB4.svg?logo=php)
@@ -49,9 +51,6 @@ activity, making it accessible only to users from specified IP addresses.
1. The users with matching ip addresses can view the activity. 1. The users with matching ip addresses can view the activity.
5. Save the activity 5. Save the activity
## TODO
- Behat tests ip validation
## Security ## Security
If you discover any security related issues, please email [[email protected]](mailto:[email protected]) instead of using the issue tracker. If you discover any security related issues, please email [[email protected]](mailto:[email protected]) instead of using the issue tracker.
+36
View File
@@ -0,0 +1,36 @@
# Security Policy
At `Ldesign Media`, we take the security of our software very seriously. This policy outlines the steps we take to ensure the security of our software and how you can help us keep it secure.
## Supported Versions
To ensure the best security for our users, we only provide support for the latest version of our software.
## Reporting a Vulnerability
If you discover a security vulnerability in our software, we kindly ask that you report it to us as soon as possible by sending an email to `[email protected]`. Please do not use the issue tracker for reporting security vulnerabilities.
We will make every effort to investigate and resolve the vulnerability in a timely manner.
When reporting a vulnerability, please include the following information (as much as you can provide) to help us better understand the nature and scope of the issue:
- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
- Full paths of source file(s) related to the manifestation of the issue
- The location of the affected source code (tag/branch/commit or direct URL)
- Any special configuration required to reproduce the issue
- Step-by-step instructions to reproduce the issue
- Proof-of-concept or exploit code (if possible)
- Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
## Rewards
We do not currently have a bug bounty program, but we are grateful for any responsible disclosures of security vulnerabilities. Your efforts help make our software safer for everyone.
## Communication
When a security vulnerability has been discovered and addressed, we will provide updates to the community as soon as possible to keep everyone informed.
## Acknowledgements
We would like to extend our gratitude to the security researchers who have helped us improve the security of our software by responsibly reporting security vulnerabilities to us. Your contributions are invaluable to maintaining a safe and secure environment for our users.
+5 -44
View File
@@ -25,6 +25,8 @@
namespace availability_ipaddress\form; namespace availability_ipaddress\form;
use availability_ipaddress\helper;
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
global $CFG; global $CFG;
@@ -102,52 +104,11 @@ class range_form extends \moodleform {
public function validation($data, $files) { public function validation($data, $files) {
$errors = parent::validation($data, $files); $errors = parent::validation($data, $files);
// Validate IP addresses. // Validate IP addresses, the field is required so an empty value is invalid as well.
if (!empty($data['ipaddresses'])) { if (!helper::is_valid_ipaddress_list($data['ipaddresses'] ?? '')) {
$ipaddresses = explode(',', $data['ipaddresses']); $errors['ipaddresses'] = get_string('error_ipaddress', 'availability_ipaddress');
foreach ($ipaddresses as $ip) {
$ip = trim($ip);
if (!$this->validate_ip_format($ip)) {
$errors['ipaddresses'] = get_string('error_ipaddress', 'availability_ipaddress');
break;
}
}
} }
return $errors; 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;
}
} }
+137
View File
@@ -359,4 +359,141 @@ class helper {
return $modified; 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
View File
@@ -7,12 +7,12 @@
<TABLE NAME="availability_ipaddress_pre" COMMENT="Stores predefined IP address ranges for availability_ipaddress"> <TABLE NAME="availability_ipaddress_pre" COMMENT="Stores predefined IP address ranges for availability_ipaddress">
<FIELDS> <FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/> <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="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"/> <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"/> <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"/> <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"/> <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0"/> <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS> </FIELDS>
<KEYS> <KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/> <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+6 -5
View File
@@ -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> $string['ipaddresses_help_help'] = '<p>Enter one or more IP addresses or ranges, separated by commas.</p>
<p><strong>Examples:</strong></p> <p><strong>Examples:</strong></p>
<ul> <ul>
<li><strong>Single IP:</strong> <code>192.168.1.1</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></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></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> <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['enabled'] = 'Enabled';
$string['sortorder'] = 'Sort order'; $string['sortorder'] = 'Sort order';
$string['existing_ranges'] = 'Existing IP ranges'; $string['existing_ranges'] = 'Existing IP ranges';
+98
View File
@@ -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
View File
@@ -27,8 +27,8 @@
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
$plugin->component = 'availability_ipaddress'; $plugin->component = 'availability_ipaddress';
$plugin->version = 2026031800; $plugin->version = 2026101500;
$plugin->release = '5.1.1'; $plugin->release = '5.3.2';
$plugin->requires = 2016120500; $plugin->requires = 2016120500;
$plugin->maturity = MATURITY_STABLE; $plugin->maturity = MATURITY_STABLE;
$plugin->supported = [400, 501]; $plugin->supported = [400, 503];
@@ -15,11 +15,12 @@ YUI.add('moodle-availability_ipaddress-form', function (Y, NAME) {
*/ */
M.availability_ipaddress = M.availability_ipaddress || {}; 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 // MIT https://github.com/sindresorhus/ip-regex
// Advanced ip-address regex for validating. // IPv6 address, without anchors so it can be combined.
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]' + 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]" +
'[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}" + "\\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})" + "(?::(?: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]" + "{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]" + "\\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}|:)|(?::" + "|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}){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 * @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} * @returns {boolean}
*/ */
M.availability_ipaddress.validateIpaddress = function(ipaddresses) { M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
'use strict'; 'use strict';
Y.log(ipaddresses); var entries, i;
// Return true for empty string - it's valid to have no custom IPs
if (!ipaddresses || ipaddresses.trim() === '') { // Return true for empty string - it's valid to have no custom IPs.
if (!ipaddresses || M.availability_ipaddress.trim(ipaddresses) === '') {
return true; return true;
} }
ipaddresses = ipaddresses.split(',');
for (var i in ipaddresses) {
// Test normal ip format. entries = ipaddresses.split(',');
// Strict ipv4 check. for (i = 0; i < entries.length; i++) {
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) // Whitespace around an entry is allowed, address_in_subnet() trims it as well.
.test(ipaddresses[i])) { if (!M.availability_ipaddress.validateEntry(M.availability_ipaddress.trim(entries[i]))) {
Y.log('Correct ipv4'); Y.log('Incorrect ip-address: ' + entries[i]);
continue; return false;
} }
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');
return false;
} }
Y.log('Valid ipaddresses');
return true; return true;
}; };
@@ -259,9 +341,8 @@ M.availability_ipaddress.form.fillErrors = function(errors, node) {
var value = {}; var value = {};
this.fillValue(value, node); this.fillValue(value, node);
// Basic ipaddresses checks - only validate if not empty. // Basic ipaddresses checks - an empty value is valid.
if (value.ipaddresses && value.ipaddresses.trim() !== '' && if (M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
errors.push('availability_ipaddress:error_ipaddress'); errors.push('availability_ipaddress:error_ipaddress');
} }
}; };
@@ -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
@@ -15,11 +15,12 @@ YUI.add('moodle-availability_ipaddress-form', function (Y, NAME) {
*/ */
M.availability_ipaddress = M.availability_ipaddress || {}; 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 // MIT https://github.com/sindresorhus/ip-regex
// Advanced ip-address regex for validating. // IPv6 address, without anchors so it can be combined.
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]' + 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]" +
'[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}" + "\\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})" + "(?::(?: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]" + "{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]" + "\\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}|:)|(?::" + "|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}){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 * @class M.availability_ipaddress.form
@@ -160,51 +161,138 @@ 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} * @returns {boolean}
*/ */
M.availability_ipaddress.validateIpaddress = function(ipaddresses) { M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
'use strict'; 'use strict';
// Return true for empty string - it's valid to have no custom IPs var entries, i;
if (!ipaddresses || ipaddresses.trim() === '') {
// Return true for empty string - it's valid to have no custom IPs.
if (!ipaddresses || M.availability_ipaddress.trim(ipaddresses) === '') {
return true; return true;
} }
ipaddresses = ipaddresses.split(',');
for (var i in ipaddresses) {
// Test normal ip format. entries = ipaddresses.split(',');
// Strict ipv4 check. for (i = 0; i < entries.length; i++) {
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) // Whitespace around an entry is allowed, address_in_subnet() trims it as well.
.test(ipaddresses[i])) { if (!M.availability_ipaddress.validateEntry(M.availability_ipaddress.trim(entries[i]))) {
continue; return false;
} }
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;
}
return false;
} }
return true; return true;
@@ -249,9 +337,8 @@ M.availability_ipaddress.form.fillErrors = function(errors, node) {
var value = {}; var value = {};
this.fillValue(value, node); this.fillValue(value, node);
// Basic ipaddresses checks - only validate if not empty. // Basic ipaddresses checks - an empty value is valid.
if (value.ipaddresses && value.ipaddresses.trim() !== '' && if (M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
errors.push('availability_ipaddress:error_ipaddress'); errors.push('availability_ipaddress:error_ipaddress');
} }
}; };
+132 -51
View File
@@ -13,11 +13,12 @@
*/ */
M.availability_ipaddress = M.availability_ipaddress || {}; 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 // MIT https://github.com/sindresorhus/ip-regex
// Advanced ip-address regex for validating. // IPv6 address, without anchors so it can be combined.
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]' + 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]" +
'[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}" + "\\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})" + "(?::(?: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]" + "{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]" + "\\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}|:)|(?::" + "|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}){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 * @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} * @returns {boolean}
*/ */
M.availability_ipaddress.validateIpaddress = function(ipaddresses) { M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
'use strict'; 'use strict';
Y.log(ipaddresses); var entries, i;
// Return true for empty string - it's valid to have no custom IPs
if (!ipaddresses || ipaddresses.trim() === '') { // Return true for empty string - it's valid to have no custom IPs.
if (!ipaddresses || M.availability_ipaddress.trim(ipaddresses) === '') {
return true; return true;
} }
ipaddresses = ipaddresses.split(',');
for (var i in ipaddresses) {
// Test normal ip format. entries = ipaddresses.split(',');
// Strict ipv4 check. for (i = 0; i < entries.length; i++) {
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) // Whitespace around an entry is allowed, address_in_subnet() trims it as well.
.test(ipaddresses[i])) { if (!M.availability_ipaddress.validateEntry(M.availability_ipaddress.trim(entries[i]))) {
Y.log('Correct ipv4'); Y.log('Incorrect ip-address: ' + entries[i]);
continue; return false;
} }
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');
return false;
} }
Y.log('Valid ipaddresses');
return true; return true;
}; };
@@ -257,9 +339,8 @@ M.availability_ipaddress.form.fillErrors = function(errors, node) {
var value = {}; var value = {};
this.fillValue(value, node); this.fillValue(value, node);
// Basic ipaddresses checks - only validate if not empty. // Basic ipaddresses checks - an empty value is valid.
if (value.ipaddresses && value.ipaddresses.trim() !== '' && if (M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
errors.push('availability_ipaddress:error_ipaddress'); errors.push('availability_ipaddress:error_ipaddress');
} }
}; };