From 38500021b6a567418dc88cabec5fff7d50c8850e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 27 Mar 2015 14:48:04 -0700 Subject: [PATCH 1/8] GUAC-1138: Add filter pattern tokenizer. Match any object property against all tokens. --- .../webapp/app/list/types/FilterPattern.js | 71 ++++++-- .../main/webapp/app/list/types/FilterToken.js | 153 ++++++++++++++++++ 2 files changed, 207 insertions(+), 17 deletions(-) create mode 100644 guacamole/src/main/webapp/app/list/types/FilterToken.js diff --git a/guacamole/src/main/webapp/app/list/types/FilterPattern.js b/guacamole/src/main/webapp/app/list/types/FilterPattern.js index 82fdfc27e..61192b84e 100644 --- a/guacamole/src/main/webapp/app/list/types/FilterPattern.js +++ b/guacamole/src/main/webapp/app/list/types/FilterPattern.js @@ -23,8 +23,14 @@ /** * A service for defining the FilterPattern class. */ -angular.module('list').factory('FilterPattern', ['$parse', - function defineFilterPattern($parse) { +angular.module('list').factory('FilterPattern', ['$injector', + function defineFilterPattern($injector) { + + // Required types + var FilterToken = $injector.get('FilterToken'); + + // Required services + var $parse = $injector.get('$parse'); /** * Object which handles compilation of filtering predicates as used by @@ -69,6 +75,43 @@ angular.module('list').factory('FilterPattern', ['$parse', getters.push($parse(expression)); }); + /** + * Determines whether the given object matches the given filter pattern + * token. + * + * @param {Object} object + * The object to match the token against. + * + * @param {FilterToken} token + * The token from the tokenized filter pattern to match aginst the + * given object. + * + * @returns {Boolean} + * true if the object matches the token, false otherwise. + */ + var matchesToken = function matchToken(object, token) { + + // Only match against literals + if (token.type !== 'LITERAL') + return false; + + // For each defined getter + for (var i=0; i < getters.length; i++) { + + // Retrieve value of current getter + var value = getters[i](object); + + // If the value matches the pattern, the whole object matches + if (String(value).toLowerCase().indexOf(token.value) !== -1) + return true; + + } + + // No matches found + return false; + + }; + /** * The current filtering predicate. * @@ -92,26 +135,20 @@ angular.module('list').factory('FilterPattern', ['$parse', return; } - // Convert to lower case for case insensitive matching - pattern = pattern.toLowerCase(); + // Tokenize pattern, converting to lower case for case-insensitive matching + var tokens = FilterToken.tokenize(pattern.toLowerCase()); // Return predicate which matches against the value of any getter in the getters array - filterPattern.predicate = function matchAny(object) { - - // For each defined getter - for (var i=0; i < getters.length; i++) { - - // Retrieve value of current getter - var value = getters[i](object); - - // If the value matches the pattern, the whole object matches - if (String(value).toLowerCase().indexOf(pattern) !== -1) - return true; + filterPattern.predicate = function matchesAllTokens(object) { + // False if any token does not match + for (var i=0; i < tokens.length; i++) { + if (!matchesToken(object, tokens[i])) + return false; } - // No matches found - return false; + // True if all tokens matched + return true; }; diff --git a/guacamole/src/main/webapp/app/list/types/FilterToken.js b/guacamole/src/main/webapp/app/list/types/FilterToken.js new file mode 100644 index 000000000..ae8aa1881 --- /dev/null +++ b/guacamole/src/main/webapp/app/list/types/FilterToken.js @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * A service for defining the FilterToken class. + */ +angular.module('list').factory('FilterToken', [ + function defineFilterToken() { + + /** + * An arbitrary token having an associated type and string value. + * + * @constructor + * @param {String} type + * The type of this token. Each legal type name is a property within + * FilterToken.Types. + * + * @param {String} value + * The string value of this token. + */ + var FilterToken = function FilterToken(type, value) { + + /** + * The type of this token. Each legal type name is a property within + * FilterToken.Types. + * + * @type String + */ + this.type = type; + + /** + * The string value of this token. + * + * @type String + */ + this.value = value; + + }; + + /** + * All legal token types, and corresponding regular expressions which match + * them. If the regular expression contains capturing groups, the last + * matching group will be used as the value of the token. + * + * @type Object. + */ + FilterToken.Types = { + + /** + * A string literal. + */ + LITERAL: /^"([^"]*)"|^\S+/, + + /** + * Arbitrary contiguous whitespace. + */ + WHITESPACE: /^\s+/ + + }; + + /** + * Tokenizes the given string, returning an array of tokens. Whitespace + * tokens are dropped. + * + * @param {String} str + * The string to tokenize. + * + * @returns {FilterToken[]} + * All tokens identified within the given string, in order. + */ + FilterToken.tokenize = function tokenize(str) { + + var tokens = []; + + /** + * Returns the first token on the current string, removing the token + * from that string. + * + * @returns FilterToken + * The first token on the string, or null if no tokens match. + */ + var popToken = function popToken() { + + // Attempt to find a matching token + for (var type in FilterToken.Types) { + + // Get regular expression for current type + var regex = FilterToken.Types[type]; + + // If token matches, return the matching group + var match = regex.exec(str); + if (match) { + + // Advance to next token + str = str.substring(match[0].length); + + // Grab last matching group + var matchingGroup = match[0]; + for (var i=1; i < match.length; i++) + matchingGroup = match[i] || matchingGroup; + + // Return new token + return new FilterToken(type, matchingGroup); + + } + + } + + // No match + return null; + + }; + + // Tokenize input until no input remains + while (str) { + + // Remove first token + var token = popToken(); + if (!token) + break; + + // Add token to tokens array, if not whitespace + if (token.type !== 'WHITESPACE') + tokens.push(token); + + } + + return tokens; + + }; + + return FilterToken; + +}]); \ No newline at end of file From 9dd613261e6cc295a773bbfe7e2c72b056f68dac Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 27 Mar 2015 17:15:28 -0700 Subject: [PATCH 2/8] GUAC-1138: Add class for parsing and comparing IPv4 networks. --- .../main/webapp/app/list/types/IPv4Network.js | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 guacamole/src/main/webapp/app/list/types/IPv4Network.js diff --git a/guacamole/src/main/webapp/app/list/types/IPv4Network.js b/guacamole/src/main/webapp/app/list/types/IPv4Network.js new file mode 100644 index 000000000..6ee8ff1f8 --- /dev/null +++ b/guacamole/src/main/webapp/app/list/types/IPv4Network.js @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * A service for defining the IPv4Network class. + */ +angular.module('list').factory('IPv4Network', [ + function defineIPv4Network() { + + /** + * Represents an IPv4 network as a pairing of base address and netmask, + * both of which are in binary form. To obtain an IPv4Network from + * standard CIDR or dot-decimal notation, use IPv4Network.parse(). + * + * @constructor + * @param {Number} address + * The IPv4 address of the network in binary form. + * + * @param {Number} netmask + * The IPv4 netmask of the network in binary form. + */ + var IPv4Network = function IPv4Network(address, netmask) { + + /** + * Reference to this IPv4Network. + * + * @type IPv4Network + */ + var network = this; + + /** + * The binary address of this network. This will be a 32-bit quantity. + * + * @type Number + */ + this.address = address; + + /** + * The binary netmask of this network. This will be a 32-bit quantity. + * + * @type Number + */ + this.netmask = netmask; + + /** + * Tests whether the given network is entirely within this network, + * taking into account the base addresses and netmasks of both. + * + * @param {IPv4Network} other + * The network to test. + * + * @returns {Boolean} + * true if the other network is entirely within this network, false + * otherwise. + */ + this.contains = function contains(other) { + return network.address === (other.address & other.netmask & network.netmask); + }; + + }; + + /** + * Parses the given string as an IPv4 address or subnet, returning an + * IPv4Network object which describes that address or subnet. + * + * @param {String} str + * The string to parse. + * + * @returns {IPv4Network} + * The parsed network, or null if the given string is not valid. + */ + IPv4Network.parse = function parse(str) { + + // Regex which matches the general form of IPv4 addresses + var pattern = /^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(?:\/([0-9]{1,2}))?$/; + + // Parse IPv4 address via regex + var match = pattern.exec(str); + if (!match) + return null; + + // Parse netmask, if given + var netmask = 0xFFFFFFFF; + if (match[5]) { + var bits = parseInt(match[5]); + if (bits > 0 && bits <= 32) + netmask = 0xFFFFFFFF << (32 - bits); + } + + // Read each octet onto address + var address = 0; + for (var i=1; i <= 4; i++) { + + // Validate octet range + var octet = parseInt(match[i]); + if (octet > 255) + return null; + + // Shift on octet + address = (address << 8) | octet; + + } + + return new IPv4Network(address, netmask); + + }; + + return IPv4Network; + +}]); From 6ce06661a6a3a8edfce2d54000ece1ed0c3b9684 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 27 Mar 2015 20:41:00 -0700 Subject: [PATCH 3/8] GUAC-1138: Add class for parsing and comparing IPv6 networks. --- .../main/webapp/app/list/types/IPv6Network.js | 230 ++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 guacamole/src/main/webapp/app/list/types/IPv6Network.js diff --git a/guacamole/src/main/webapp/app/list/types/IPv6Network.js b/guacamole/src/main/webapp/app/list/types/IPv6Network.js new file mode 100644 index 000000000..b1a9a4643 --- /dev/null +++ b/guacamole/src/main/webapp/app/list/types/IPv6Network.js @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * A service for defining the IPv6Network class. + */ +angular.module('list').factory('IPv6Network', [ + function defineIPv6Network() { + + /** + * Represents an IPv6 network as a pairing of base address and netmask, + * both of which are in binary form. To obtain an IPv6Network from + * standard CIDR or dot-decimal notation, use IPv6Network.parse(). + * + * @constructor + * @param {Number[]} addressGroups + * Array of eight IPv6 address groups in binary form, each group being + * 16-bit number. + * + * @param {Number[]} netmaskGroups + * Array of eight IPv6 netmask groups in binary form, each group being + * 16-bit number. + */ + var IPv6Network = function IPv6Network(addressGroups, netmaskGroups) { + + /** + * Reference to this IPv6Network. + * + * @type IPv6Network + */ + var network = this; + + /** + * The 128-bit binary address of this network as an array of eight + * 16-bit numbers. + * + * @type Number[] + */ + this.addressGroups = addressGroups; + + /** + * The 128-bit binary netmask of this network as an array of eight + * 16-bit numbers. + * + * @type Number + */ + this.netmaskGroups = netmaskGroups; + + /** + * Tests whether the given network is entirely within this network, + * taking into account the base addresses and netmasks of both. + * + * @param {IPv6Network} other + * The network to test. + * + * @returns {Boolean} + * true if the other network is entirely within this network, false + * otherwise. + */ + this.contains = function contains(other) { + + // Test that each masked 16-bit quantity matches the address + for (var i=0; i < 8; i++) { + if (network.addressGroups[i] !== (other.addressGroups[i] + & other.netmaskGroups[i] + & network.netmaskGroups[i])) + return false; + } + + // All 16-bit numbers match + return true; + + }; + + }; + + /** + * Generates a netmask having the given number of ones on the left side. + * All other bits within the netmask will be zeroes. The resulting netmask + * will be an array of eight numbers, where each number corresponds to a + * 16-bit group of an IPv6 netmask. + * + * @param {Number} bits + * The number of ones to include on the left side of the netmask. All + * other bits will be zeroes. + * + * @returns {Number[]} + * The generated netmask, having the given number of ones. + */ + var generateNetmask = function generateNetmask(bits) { + + var netmask = []; + + // Only generate up to 128 bits + bits = Math.min(128, bits); + + // Add any contiguous 16-bit sections of 1's + while (bits >= 16) { + netmask.push(0xFFFF); + bits -= 16; + } + + // Add remaining 1's + if (bits > 0 && bits <= 16) + netmask.push(0xFFFF & (0xFFFF << (16 - bits))); + + // Add remaining zeroes + while (netmask.length < 8) + netmask.push(0); + + return netmask; + + }; + + /** + * Splits the given IPv6 address or partial address into its corresponding + * 16-bit groups. + * + * @param {String} str + * The IPv6 address or partial address to split. + * + * @returns Number[] + * The numeric values of all 16-bit groups within the given IPv6 + * address. + */ + var splitAddress = function splitAddress(str) { + + var address = []; + + // Split address into groups + var groups = str.split(':'); + + // Parse the numeric value of each group + angular.forEach(groups, function addGroup(group) { + var value = parseInt(group || '0', 16); + address.push(value); + }); + + return address; + + }; + + /** + * Parses the given string as an IPv6 address or subnet, returning an + * IPv6Network object which describes that address or subnet. + * + * @param {String} str + * The string to parse. + * + * @returns {IPv6Network} + * The parsed network, or null if the given string is not valid. + */ + IPv6Network.parse = function parse(str) { + + // Regex which matches the general form of IPv6 addresses + var pattern = /^([0-9a-f]{0,4}(?::[0-9a-f]{0,4}){0,7})(?:\/([0-9]{1,3}))?$/; + + // Parse rudimentary IPv6 address via regex + var match = pattern.exec(str); + if (!match) + return null; + + // Extract address and netmask from parse results + var unparsedAddress = match[1]; + var unparsedNetmask = match[2]; + + // Parse netmask + var netmask; + if (unparsedNetmask) + netmask = generateNetmask(parseInt(unparsedNetmask)); + else + netmask = generateNetmask(128); + + var address; + + // Separate based on the double-colon, if present + var doubleColon = unparsedAddress.indexOf('::'); + + // If no double colon, just split into groups + if (doubleColon === -1) + address = splitAddress(unparsedAddress); + + // Otherwise, split either side of the double colon and pad with zeroes + else { + + // Parse either side of the double colon + var leftAddress = splitAddress(unparsedAddress.substring(0, doubleColon)); + var rightAddress = splitAddress(unparsedAddress.substring(doubleColon + 2)); + + // Pad with zeroes up to address length + var remaining = 8 - leftAddress.length - rightAddress.length; + while (remaining > 0) { + leftAddress.push(0); + remaining--; + } + + address = leftAddress.concat(rightAddress); + + } + + // Validate length of address + if (address.length !== 8) + return null; + + return new IPv6Network(address, netmask); + + }; + + return IPv6Network; + +}]); From ef89c74c5d6069bcabf6480569a4b240e85dacea Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 27 Mar 2015 20:56:46 -0700 Subject: [PATCH 4/8] GUAC-1138: Use parsing functions for tokens, not necessarily regex. --- .../main/webapp/app/list/types/FilterToken.js | 91 +++++++++++++------ 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/guacamole/src/main/webapp/app/list/types/FilterToken.js b/guacamole/src/main/webapp/app/list/types/FilterToken.js index ae8aa1881..d89839f5b 100644 --- a/guacamole/src/main/webapp/app/list/types/FilterToken.js +++ b/guacamole/src/main/webapp/app/list/types/FilterToken.js @@ -27,17 +27,28 @@ angular.module('list').factory('FilterToken', [ function defineFilterToken() { /** - * An arbitrary token having an associated type and string value. + * An arbitrary token having an associated type and value. * * @constructor + * @param {String} consumed + * The input string consumed to produce this token. + * * @param {String} type * The type of this token. Each legal type name is a property within * FilterToken.Types. * - * @param {String} value - * The string value of this token. + * @param {Object} value + * The value of this token. The type of this value is determined by + * the token type. */ - var FilterToken = function FilterToken(type, value) { + var FilterToken = function FilterToken(consumed, type, value) { + + /** + * The input string that was consumed to produce this token. + * + * @type String + */ + this.consumed = consumed; /** * The type of this token. Each legal type name is a property within @@ -48,32 +59,62 @@ angular.module('list').factory('FilterToken', [ this.type = type; /** - * The string value of this token. + * The value of this token. * - * @type String + * @type Object */ this.value = value; }; /** - * All legal token types, and corresponding regular expressions which match - * them. If the regular expression contains capturing groups, the last - * matching group will be used as the value of the token. + * All legal token types, and corresponding functions which match them. + * Each function returns the parsed token, or null if no such token was + * found. * - * @type Object. + * @type Object. */ FilterToken.Types = { /** - * A string literal. + * A string literal, which may be quoted. The value of a LITERAL token + * is a String. */ - LITERAL: /^"([^"]*)"|^\S+/, + LITERAL: function parseLiteral(str) { + + var pattern = /^"([^"]*)"|^\S+/; + + // Validate against pattern + var matches = pattern.exec(str); + if (!matches) + return null; + + // If literal is quoted, parse within the quotes + if (matches[1]) + return new FilterToken(matches[0], 'LITERAL', matches[1]); + + // Otherwise, literal is unquoted + return new FilterToken(matches[0], 'LITERAL', matches[0]); + + }, /** - * Arbitrary contiguous whitespace. + * Arbitrary contiguous whitespace. The value of a WHITESPACE token is + * a String. */ - WHITESPACE: /^\s+/ + WHITESPACE: function parseWhitespace(str) { + + var pattern = /^\s+/; + + // Validate against pattern + var matches = pattern.exec(str); + if (!matches) + return null; + + // Generate token from matching whitespace + return new FilterToken(matches[0], 'WHITESPACE', matches[0]); + + } }; @@ -103,24 +144,14 @@ angular.module('list').factory('FilterToken', [ // Attempt to find a matching token for (var type in FilterToken.Types) { - // Get regular expression for current type - var regex = FilterToken.Types[type]; + // Get matching function for current type + var matcher = FilterToken.Types[type]; // If token matches, return the matching group - var match = regex.exec(str); - if (match) { - - // Advance to next token - str = str.substring(match[0].length); - - // Grab last matching group - var matchingGroup = match[0]; - for (var i=1; i < match.length; i++) - matchingGroup = match[i] || matchingGroup; - - // Return new token - return new FilterToken(type, matchingGroup); - + var token = matcher(str); + if (token) { + str = str.substring(token.consumed.length); + return token; } } From de5bed964925870ad89d9970d17576a7a9cbd9b2 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 27 Mar 2015 21:50:03 -0700 Subject: [PATCH 5/8] GUAC-1138: Add support for IPv4 and IPv6 network tokens. --- .../main/webapp/app/list/types/FilterToken.js | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/guacamole/src/main/webapp/app/list/types/FilterToken.js b/guacamole/src/main/webapp/app/list/types/FilterToken.js index d89839f5b..915f072ea 100644 --- a/guacamole/src/main/webapp/app/list/types/FilterToken.js +++ b/guacamole/src/main/webapp/app/list/types/FilterToken.js @@ -23,8 +23,12 @@ /** * A service for defining the FilterToken class. */ -angular.module('list').factory('FilterToken', [ - function defineFilterToken() { +angular.module('list').factory('FilterToken', ['$injector', + function defineFilterToken($injector) { + + // Required types + var IPv4Network = $injector.get('IPv4Network'); + var IPv6Network = $injector.get('IPv6Network'); /** * An arbitrary token having an associated type and value. @@ -76,6 +80,50 @@ angular.module('list').factory('FilterToken', [ */ FilterToken.Types = { + /** + * An IPv4 address or subnet. The value of an IPV4_NETWORK token is an + * IPv4Network. + */ + IPV4_NETWORK: function parseIPv4(str) { + + var pattern = /^\S+/; + + // Read first word via regex + var matches = pattern.exec(str); + if (!matches) + return null; + + // Validate and parse as IPv4 address + var network = IPv4Network.parse(matches[0]); + if (!network) + return null; + + return new FilterToken(matches[0], 'IPV4_NETWORK', network); + + }, + + /** + * An IPv6 address or subnet. The value of an IPV6_NETWORK token is an + * IPv6Network. + */ + IPV6_NETWORK: function parseIPv6(str) { + + var pattern = /^\S+/; + + // Read first word via regex + var matches = pattern.exec(str); + if (!matches) + return null; + + // Validate and parse as IPv6 address + var network = IPv6Network.parse(matches[0]); + if (!network) + return null; + + return new FilterToken(matches[0], 'IPV6_NETWORK', network); + + }, + /** * A string literal, which may be quoted. The value of a LITERAL token * is a String. From 7c5157c2b95df57c8503c45007fdd7b84121967d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 27 Mar 2015 22:08:07 -0700 Subject: [PATCH 6/8] GUAC-1138: Filter columns based on network address, if they appear to contain network addresses. --- .../webapp/app/list/types/FilterPattern.js | 125 ++++++++++++++++-- 1 file changed, 111 insertions(+), 14 deletions(-) diff --git a/guacamole/src/main/webapp/app/list/types/FilterPattern.js b/guacamole/src/main/webapp/app/list/types/FilterPattern.js index 61192b84e..fa42e2d0b 100644 --- a/guacamole/src/main/webapp/app/list/types/FilterPattern.js +++ b/guacamole/src/main/webapp/app/list/types/FilterPattern.js @@ -28,6 +28,8 @@ angular.module('list').factory('FilterPattern', ['$injector', // Required types var FilterToken = $injector.get('FilterToken'); + var IPv4Network = $injector.get('IPv4Network'); + var IPv6Network = $injector.get('IPv6Network'); // Required services var $parse = $injector.get('$parse'); @@ -75,6 +77,99 @@ angular.module('list').factory('FilterPattern', ['$injector', getters.push($parse(expression)); }); + /** + * Determines whether the given object contains properties that match + * the given string, according to the provided getters. + * + * @param {Object} object + * The object to match against. + * + * @param {String} str + * The string to match. + * + * @returns {Boolean} + * true if the object matches the given string, false otherwise. + */ + var matchesString = function matchesString(object, str) { + + // For each defined getter + for (var i=0; i < getters.length; i++) { + + // Retrieve value of current getter + var value = getters[i](object); + + // If the value matches the pattern, the whole object matches + if (String(value).toLowerCase().indexOf(str) !== -1) + return true; + + } + + // No matches found + return false; + + }; + + /** + * Determines whether the given object contains properties that match + * the given IPv4 network, according to the provided getters. + * + * @param {Object} object + * The object to match against. + * + * @param {IPv4Network} network + * The IPv4 network to match. + * + * @returns {Boolean} + * true if the object matches the given network, false otherwise. + */ + var matchesIPv4 = function matchesIPv4(object, network) { + + // For each defined getter + for (var i=0; i < getters.length; i++) { + + // Test value against IPv4 network + var value = IPv4Network.parse(String(getters[i](object))); + if (value && network.contains(value)) + return true; + + } + + // No matches found + return false; + + }; + + /** + * Determines whether the given object contains properties that match + * the given IPv6 network, according to the provided getters. + * + * @param {Object} object + * The object to match against. + * + * @param {IPv6Network} network + * The IPv6 network to match. + * + * @returns {Boolean} + * true if the object matches the given network, false otherwise. + */ + var matchesIPv6 = function matchesIPv6(object, network) { + + // For each defined getter + for (var i=0; i < getters.length; i++) { + + // Test value against IPv6 network + var value = IPv6Network.parse(String(getters[i](object))); + if (value && network.contains(value)) + return true; + + } + + // No matches found + return false; + + }; + + /** * Determines whether the given object matches the given filter pattern * token. @@ -89,27 +184,29 @@ angular.module('list').factory('FilterPattern', ['$injector', * @returns {Boolean} * true if the object matches the token, false otherwise. */ - var matchesToken = function matchToken(object, token) { + var matchesToken = function matchesToken(object, token) { - // Only match against literals - if (token.type !== 'LITERAL') - return false; + // Match depending on token type + switch (token.type) { - // For each defined getter - for (var i=0; i < getters.length; i++) { + // Simple string literal + case 'LITERAL': + return matchesString(object, token.value); - // Retrieve value of current getter - var value = getters[i](object); + // IPv4 network address / subnet + case 'IPV4_NETWORK': + return matchesIPv4(object, token.value); - // If the value matches the pattern, the whole object matches - if (String(value).toLowerCase().indexOf(token.value) !== -1) - return true; + // IPv6 network address / subnet + case 'IPV6_NETWORK': + return matchesIPv6(object, token.value); + + // Unsupported token type + default: + return false; } - // No matches found - return false; - }; /** From d65853422ce1bf676c6b7507cd6080b22f103673 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 28 Mar 2015 00:28:42 -0700 Subject: [PATCH 7/8] GUAC-1138: There is no dot-decimal notation in IPv6. Incorrect comment. --- guacamole/src/main/webapp/app/list/types/IPv6Network.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guacamole/src/main/webapp/app/list/types/IPv6Network.js b/guacamole/src/main/webapp/app/list/types/IPv6Network.js index b1a9a4643..5d7ad9515 100644 --- a/guacamole/src/main/webapp/app/list/types/IPv6Network.js +++ b/guacamole/src/main/webapp/app/list/types/IPv6Network.js @@ -29,7 +29,7 @@ angular.module('list').factory('IPv6Network', [ /** * Represents an IPv6 network as a pairing of base address and netmask, * both of which are in binary form. To obtain an IPv6Network from - * standard CIDR or dot-decimal notation, use IPv6Network.parse(). + * standard CIDR notation, use IPv6Network.parse(). * * @constructor * @param {Number[]} addressGroups From cc96b0ee33086605548c81a89051e78bfacaa44a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 28 Mar 2015 10:25:12 -0700 Subject: [PATCH 8/8] GUAC-1138: Use "ones" instead of "1's". --- guacamole/src/main/webapp/app/list/types/IPv6Network.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guacamole/src/main/webapp/app/list/types/IPv6Network.js b/guacamole/src/main/webapp/app/list/types/IPv6Network.js index 5d7ad9515..e2b40042b 100644 --- a/guacamole/src/main/webapp/app/list/types/IPv6Network.js +++ b/guacamole/src/main/webapp/app/list/types/IPv6Network.js @@ -113,13 +113,13 @@ angular.module('list').factory('IPv6Network', [ // Only generate up to 128 bits bits = Math.min(128, bits); - // Add any contiguous 16-bit sections of 1's + // Add any contiguous 16-bit sections of ones while (bits >= 16) { netmask.push(0xFFFF); bits -= 16; } - // Add remaining 1's + // Add remaining ones if (bits > 0 && bits <= 16) netmask.push(0xFFFF & (0xFFFF << (16 - bits)));