From 3630e7800c57c08c5d7e6445ce2777468d7b66ac Mon Sep 17 00:00:00 2001 From: Virtually Nick Date: Sat, 24 Oct 2020 14:53:17 -0400 Subject: [PATCH] GUACAMOLE-760: Add validation and tests for the TimeZoneGuacamoleProperty --- .../properties/TimeZoneGuacamoleProperty.java | 19 +- .../TimeZoneGuacamolePropertyTest.java | 234 ++++++++++++++++++ 2 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 guacamole-ext/src/test/java/org/apache/guacamole/properties/TimeZoneGuacamolePropertyTest.java diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/properties/TimeZoneGuacamoleProperty.java b/guacamole-ext/src/main/java/org/apache/guacamole/properties/TimeZoneGuacamoleProperty.java index 78770213a..a294bda67 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/properties/TimeZoneGuacamoleProperty.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/properties/TimeZoneGuacamoleProperty.java @@ -20,7 +20,9 @@ package org.apache.guacamole.properties; import java.util.TimeZone; +import java.util.regex.Pattern; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleServerException; /** * A GuacamoleProperty whose value is a TimeZone. @@ -28,6 +30,12 @@ import org.apache.guacamole.GuacamoleException; public abstract class TimeZoneGuacamoleProperty implements GuacamoleProperty { + /** + * A regex that matches valid variants of GMT timezones. + */ + public static final Pattern GMT_REGEX = + Pattern.compile("^GMT([+-](0|00)((:)?00)?)?$"); + @Override public TimeZone parseValue(String value) throws GuacamoleException { @@ -36,7 +44,16 @@ public abstract class TimeZoneGuacamoleProperty return null; // Attempt to return the TimeZone of the provided string value. - return TimeZone.getTimeZone(value); + TimeZone tz = TimeZone.getTimeZone(value); + + // If the input is not GMT, but the output is GMT, the TimeZone is not + // valid and we throw an exception. + if (!GMT_REGEX.matcher(value).matches() + && GMT_REGEX.matcher(tz.getID()).matches()) + throw new GuacamoleServerException("Property \"" + getName() + + "\" does not specify a valid time zone."); + + return tz; } diff --git a/guacamole-ext/src/test/java/org/apache/guacamole/properties/TimeZoneGuacamolePropertyTest.java b/guacamole-ext/src/test/java/org/apache/guacamole/properties/TimeZoneGuacamolePropertyTest.java new file mode 100644 index 000000000..48535a10c --- /dev/null +++ b/guacamole-ext/src/test/java/org/apache/guacamole/properties/TimeZoneGuacamolePropertyTest.java @@ -0,0 +1,234 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.properties; + +import java.util.Arrays; +import java.util.List; +import org.apache.guacamole.GuacamoleException; +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * Tests that validate Time Zone property input. + */ +public class TimeZoneGuacamolePropertyTest { + + /** + * An array of valid TimeZones that should be correct parsed by the TimeZone + * property, returning either the same or synonymous zone. + */ + private static final List TZ_TEST_VALID = Arrays.asList( + "America/Los_Angeles", + "America/New_York", + "Australia/Sydney", + "Africa/Johannesburg", + "Asia/Shanghai" + ); + + /** + * An array of invalid timezone names that should be parsed to GMT, which + * should cause an exception to be thrown by the TimeZone property. + */ + private static final List TZ_TEST_INVALID = Arrays.asList( + "Chips/Guacamole", + "Chips/Queso", + "Chips/Salsa", + "Mashed/Avacado", + "Pico/De_Guayo" + ); + + /** + * An array of valid GMT specifications that should be correctly parsed + * by the TimeZone property as GMT. + */ + private static final List TZ_GMT_VALID = Arrays.asList( + "GMT", + "GMT-0000", + "GMT+000", + "GMT+00:00", + "GMT-0:00", + "GMT+0" + ); + + /** + * An array of invalid GMT specifications that should cause an exception to + * be thrown for the TimeZone property. + */ + private static final List TZ_GMT_INVALID = Arrays.asList( + "GMTx0000", + "GMT=00:00", + "GMT0:00", + "GMT+000000", + "GMT-000:000", + "GMT100" + ); + + /** + * An array of custom GMT offsets that should evaluate correctly for + * the TimeZone property. + */ + private static final List TZ_CUSTOM_VALID = Arrays.asList( + "GMT-23:59", + "GMT+01:30", + "GMT-00:30", + "GMT-11:25" + ); + + /** + * An array of invalid custom GMT offsets that should cause an exception + * to be thrown by the TimeZone property. + */ + private static final List TZ_CUSTOM_INVALID = Arrays.asList( + "GMT-9999", + "GMT+2500", + "GMT+29:30", + "GMT-1:99", + "GMT+10:65" + ); + + /** + * An example TimeZoneGuacamoleProperty for testing how various possible + * TimeZone values will be parsed. + */ + private static final TimeZoneGuacamoleProperty WHERE_IN_WORLD = + new TimeZoneGuacamoleProperty() { + + @Override + public String getName() { + return "carmen-sandiego"; + } + + }; + + /** + * Tests to verify that each of the items in this list returns a valid, + * non-GMT timezone. + * + * @throws GuacamoleException + * If a test value fails to parse correctly as a non-GMT timezone. + */ + @Test + public void testValidTZs() throws GuacamoleException { + for (String tzStr : TZ_TEST_VALID) { + String tzId = WHERE_IN_WORLD.parseValue(tzStr).getID(); + assertFalse(TimeZoneGuacamoleProperty.GMT_REGEX.matcher(tzId).matches()); + } + } + + /** + * Tests invalid time zones to make sure that they produce the desired + * result, which is an exception thrown failing to parse the value. + */ + @Test + public void testInvalidTZs() { + TZ_TEST_INVALID.forEach((tzStr) -> { + try { + String tzId = WHERE_IN_WORLD.parseValue(tzStr).getID(); + fail("Invalid TimeZoneGuacamoleProperty should fail to parse with an exception."); + } + catch (GuacamoleException e) { + String msg = e.getMessage(); + assertTrue(msg.contains("does not specify a valid time zone")); + } + }); + } + + /** + * Tests a list of strings that should be valid representations of the GMT + * time zone, throwing an exception if an invalid String is found. + * + * @throws GuacamoleException + * If the test value incorrectly fails to parse as a valid GMT string. + */ + @Test + public void testValidGMT() throws GuacamoleException { + for (String tzStr : TZ_GMT_VALID) { + String tzId = WHERE_IN_WORLD.parseValue(tzStr).getID(); + assertNotNull(tzId); + } + } + + /** + * Tests various invalid GMT representations to insure that parsing of these + * values fails and the expected GuacamoleException is thrown. + */ + @Test + public void testInvalidGMT() { + TZ_GMT_INVALID.forEach((tzStr) -> { + try { + String tzId = WHERE_IN_WORLD.parseValue(tzStr).getID(); + fail("Invalid GMT value \"" + tzStr + "\" for TimeZoneGuacamoleProperty should fail to parse with an exception."); + } + catch (GuacamoleException e) { + String msg = e.getMessage(); + assertTrue(msg.contains("does not specify a valid time zone")); + } + }); + } + + /** + * Tests several custom offsets from GMT to make sure that they are returned + * as valid TimeZone objects. + * + * @throws GuacamoleException + * If the test unexpectedly fails because a custom offset throws an + * exception as an invalid TimeZone. + */ + @Test + public void testValidCustomTz() throws GuacamoleException { + for (String tzStr : TZ_CUSTOM_VALID) { + String tzId = WHERE_IN_WORLD.parseValue(tzStr).getID(); + assertNotNull(tzId); + } + } + + /** + * Tests several invalid custom timezone offsets to make sure that they are + * not accepted as valid timezones. + */ + @Test + public void testInvalidCustomTz() { + TZ_CUSTOM_INVALID.forEach((tzStr) -> { + try { + String tzId = WHERE_IN_WORLD.parseValue(tzStr).getID(); + fail("Invalid custom time zone value \"" + tzStr + "\" for TimeZoneGuacamoleProperty should fail to parse with an exception."); + } + catch (GuacamoleException e) { + String msg = e.getMessage(); + assertTrue(msg.contains("does not specify a valid time zone")); + } + }); + } + + /** + * Tests parse of null input values to make sure the resuling parsed value + * is also null. + * + * @throws GuacamoleException + * If the test unexpectedly fails parsing a null value instead + * recognizing it as an invalid value. + */ + @Test + public void nullTzCheck() throws GuacamoleException { + assertNull(WHERE_IN_WORLD.parseValue(null)); + } + + +}