GUACAMOLE-2139: Automatically determine extension compatibility.
This commit is contained in:
5 files changed
+344
-39
No files matched your search
@@ -374,6 +374,13 @@
|
||||
<version>1.2.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JUnit for unit testing -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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.extension;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Representation of the Guacamole webapp version. This is used both to
|
||||
* represent the expected minimum webapp version required by an extension and
|
||||
* to represent the running version of Guacamole.
|
||||
*/
|
||||
public class GuacamoleVersion {
|
||||
|
||||
/**
|
||||
* Regular expression that splits a version number into its major, minor,
|
||||
* and patch components, as well as an optional version suffix.
|
||||
*/
|
||||
private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)(.+)?");
|
||||
|
||||
/**
|
||||
* The version of this copy of the Guacamole web application. This value is
|
||||
* substituted by Maven during the build.
|
||||
*/
|
||||
public static final GuacamoleVersion RUNNING_VERSION = new GuacamoleVersion("${project.version}");
|
||||
|
||||
/**
|
||||
* The first (leftmost) version number of a Guacamole version. This is the
|
||||
* number of greatest significance and denotes compatibility. If two
|
||||
* Guacamole versions have the same major number, extensions for the older
|
||||
* version should still work with the newer version.
|
||||
*/
|
||||
private final int major;
|
||||
|
||||
/**
|
||||
* The second (middle) version number of a Guacamole version. If two
|
||||
* Guacamole versions have the same major number but different minor
|
||||
* numbers, they would be expected to differ in the features available.
|
||||
*/
|
||||
private final int minor;
|
||||
|
||||
/**
|
||||
* The third (rightmost) version number of a Guacamole version. This is the
|
||||
* number of least significance and denotes minor changes or fixes. If
|
||||
* two Guacamole versions have the same major and minor numbers but
|
||||
* different patch numbers, the newer version would be expected to have
|
||||
* fixes or improvements that correct issues affecting the older version,
|
||||
* but would not be expected to have major new features.
|
||||
*/
|
||||
private final int patch;
|
||||
|
||||
/**
|
||||
* An arbitrary suffix that might be appended by downstream package
|
||||
* maintainers or vendors. A Guacamole version with a particular suffix
|
||||
* would be expected to have patches applied relative to the version without
|
||||
* the suffix but is otherwise compatible (as if the patch or minor numbers
|
||||
* differed instead of the suffix).
|
||||
*/
|
||||
private final String suffix;
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleVersion representing the version of Guacamole
|
||||
* that has the given version string. The version string must be in the
|
||||
* format "MAJOR.MINOR.PATCH" with an optional and arbitrary version
|
||||
* suffix.
|
||||
*
|
||||
* @param versionStr
|
||||
* The Guacamole version string to parse.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* If the version string is not in the correct format.
|
||||
*/
|
||||
public GuacamoleVersion(String versionStr) throws IllegalArgumentException {
|
||||
|
||||
// Use all-zeroes without suffix to represent the wildcard version
|
||||
if ("*".equals(versionStr)) {
|
||||
major = minor = patch = 0;
|
||||
suffix = null;
|
||||
}
|
||||
|
||||
// Extract major/minor/patch and optional suffix from version string
|
||||
else {
|
||||
|
||||
Matcher versionMatcher = VERSION_PATTERN.matcher(versionStr);
|
||||
if (!versionMatcher.matches())
|
||||
throw new IllegalArgumentException("Format of version "
|
||||
+ "string \"" + versionStr + "\" is not valid");
|
||||
|
||||
major = Integer.parseInt(versionMatcher.group(1));
|
||||
minor = Integer.parseInt(versionMatcher.group(2));
|
||||
patch = Integer.parseInt(versionMatcher.group(3));
|
||||
suffix = versionMatcher.group(4);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this Guacamole version is expected to be compatible with
|
||||
* an extension that requires at least the given Guacamole version.
|
||||
*
|
||||
* @param required
|
||||
* The minimum Guacamole version required.
|
||||
*
|
||||
* @return
|
||||
* true if this Guacamole version is expected to be compatible with an
|
||||
* extension requiring at least the given version, false otherwise.
|
||||
*/
|
||||
public boolean provides(GuacamoleVersion required) {
|
||||
|
||||
// Wildcard version always matches
|
||||
if (required.major == 0 && required.minor == 0 && required.patch == 0
|
||||
&& required.suffix == null)
|
||||
return true;
|
||||
|
||||
// Major version component MUST be identical
|
||||
if (this.major != required.major)
|
||||
return false;
|
||||
|
||||
// Minor version component must be the same or greater than the
|
||||
// requirement
|
||||
if (this.minor < required.minor)
|
||||
return false;
|
||||
|
||||
// If both major and minor are identical, the patch version component
|
||||
// must be the same or greater than the requirement
|
||||
if (this.minor == required.minor && this.patch < required.patch)
|
||||
return false;
|
||||
|
||||
// Any suffix denotes vendor-specific patching that must be assumed to
|
||||
// be an arbitrary requirement only satisfied by that suffix. If no
|
||||
// suffix is required, any suffix of the current Guacamole version can
|
||||
// just be ignored.
|
||||
|
||||
if (required.suffix == null)
|
||||
return true;
|
||||
|
||||
return required.suffix.equals(this.suffix);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import com.google.inject.servlet.ServletModule;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -57,27 +56,6 @@ public class ExtensionModule extends ServletModule {
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(ExtensionModule.class);
|
||||
|
||||
/**
|
||||
* The version strings of all Guacamole versions whose extensions are
|
||||
* compatible with this release.
|
||||
*/
|
||||
private static final List<String> ALLOWED_GUACAMOLE_VERSIONS =
|
||||
Collections.unmodifiableList(Arrays.asList(
|
||||
"*",
|
||||
"1.0.0",
|
||||
"1.1.0",
|
||||
"1.2.0",
|
||||
"1.3.0",
|
||||
"1.4.0",
|
||||
"1.5.0",
|
||||
"1.5.1",
|
||||
"1.5.2",
|
||||
"1.5.3",
|
||||
"1.5.4",
|
||||
"1.5.5",
|
||||
"1.6.0"
|
||||
));
|
||||
|
||||
/**
|
||||
* The name of the directory within GUACAMOLE_HOME containing any .jars
|
||||
* which should be included in the classpath of all extensions.
|
||||
@@ -394,21 +372,6 @@ public class ExtensionModule extends ServletModule {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given version of Guacamole is compatible with this
|
||||
* version of Guacamole as far as extensions are concerned.
|
||||
*
|
||||
* @param guacamoleVersion
|
||||
* The version of Guacamole the extension was built for.
|
||||
*
|
||||
* @return
|
||||
* true if the given version of Guacamole is compatible with this
|
||||
* version of Guacamole, false otherwise.
|
||||
*/
|
||||
private boolean isCompatible(String guacamoleVersion) {
|
||||
return ALLOWED_GUACAMOLE_VERSIONS.contains(guacamoleVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of identifiers of all authentication providers whose
|
||||
* internal failures should be tolerated during the authentication process.
|
||||
@@ -513,7 +476,8 @@ public class ExtensionModule extends ServletModule {
|
||||
Extension extension = new Extension(getParentClassLoader(), extensionFile, temporaryFiles);
|
||||
|
||||
// Validate Guacamole version of extension
|
||||
if (!isCompatible(extension.getGuacamoleVersion())) {
|
||||
GuacamoleVersion requiredVersion = new GuacamoleVersion(extension.getGuacamoleVersion());
|
||||
if (!GuacamoleVersion.RUNNING_VERSION.provides(requiredVersion)) {
|
||||
logger.debug("Declared Guacamole version \"{}\" of extension \"{}\" is not compatible with this version of Guacamole.",
|
||||
extension.getGuacamoleVersion(), extensionFile.getName());
|
||||
throw new GuacamoleServerException("Extension \"" + extension.getName() + "\" is not "
|
||||
@@ -523,7 +487,7 @@ public class ExtensionModule extends ServletModule {
|
||||
extensions.add(extension);
|
||||
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
catch (GuacamoleException | IllegalArgumentException e) {
|
||||
logger.error("Extension \"{}\" could not be loaded: {}",
|
||||
extensionFile.getName(), e.getMessage(), e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.extension;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests which verify GuacamoleVersion correctly parses and compares
|
||||
* Guacamole versions.
|
||||
*/
|
||||
public class GuacamoleVersionTest {
|
||||
|
||||
/**
|
||||
* Verifies that trivially valid version numbers are correctly parsed.
|
||||
*/
|
||||
@Test
|
||||
public void testValidVersion() {
|
||||
GuacamoleVersion version = new GuacamoleVersion("1.2.3");
|
||||
Assert.assertTrue(version.provides(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that invalid version numbers correctly fail to parse.
|
||||
*/
|
||||
@Test
|
||||
public void testInvalidVersion() {
|
||||
for (String invalid : Arrays.asList("1.2")) {
|
||||
try {
|
||||
GuacamoleVersion version = new GuacamoleVersion(invalid);
|
||||
Assert.fail("Invalid Guacamole version string \""
|
||||
+ invalid + "\" should have failed to parse.");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that newer versions are recognized as providing older versions
|
||||
* when they differ by minor number (but not the other way around).
|
||||
*/
|
||||
@Test
|
||||
public void testMinorNewerProvidesOlder() {
|
||||
|
||||
GuacamoleVersion olderVersion = new GuacamoleVersion("1.2.3");
|
||||
GuacamoleVersion newerVersion = new GuacamoleVersion("1.22.3");
|
||||
|
||||
Assert.assertTrue(newerVersion.provides(olderVersion));
|
||||
Assert.assertFalse(olderVersion.provides(newerVersion));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that newer versions are recognized as providing older versions
|
||||
* when they differ by patch number (but not the other way around).
|
||||
*/
|
||||
@Test
|
||||
public void testPatchNewerProvidesOlder() {
|
||||
|
||||
GuacamoleVersion olderVersion = new GuacamoleVersion("1.2.3");
|
||||
GuacamoleVersion newerVersion = new GuacamoleVersion("1.2.7");
|
||||
|
||||
Assert.assertTrue(newerVersion.provides(olderVersion));
|
||||
Assert.assertFalse(olderVersion.provides(newerVersion));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that two versions are NOT recognized as providing each other
|
||||
* when they differ by major number (regardless of which is newer).
|
||||
*/
|
||||
@Test
|
||||
public void testPatchMajorIncompatible() {
|
||||
|
||||
GuacamoleVersion olderVersion = new GuacamoleVersion("1.2.3");
|
||||
GuacamoleVersion newerVersion = new GuacamoleVersion("2.2.3");
|
||||
|
||||
Assert.assertFalse(newerVersion.provides(olderVersion));
|
||||
Assert.assertFalse(olderVersion.provides(newerVersion));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a version number with an arbitrary suffix is recognized
|
||||
* as providing that same version without the suffix (but not the other way
|
||||
* around).
|
||||
*/
|
||||
@Test
|
||||
public void testCompatibleSuffix() {
|
||||
|
||||
GuacamoleVersion olderVersion = new GuacamoleVersion("1.2.3");
|
||||
GuacamoleVersion newerVersion = new GuacamoleVersion("1.2.3-foo");
|
||||
|
||||
Assert.assertTrue(newerVersion.provides(olderVersion));
|
||||
Assert.assertFalse(olderVersion.provides(newerVersion));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that two version numbers that differ by arbitrary suffix are
|
||||
* NOT recognized as providing each other, even when the version numbers
|
||||
* are otherwise the same.
|
||||
*/
|
||||
@Test
|
||||
public void testIncompatibleSuffix() {
|
||||
|
||||
GuacamoleVersion olderVersion = new GuacamoleVersion("1.2.3-bar");
|
||||
GuacamoleVersion newerVersion = new GuacamoleVersion("1.2.3-foo");
|
||||
|
||||
Assert.assertFalse(newerVersion.provides(olderVersion));
|
||||
Assert.assertFalse(olderVersion.provides(newerVersion));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the special wildcard version is provided by any version,
|
||||
* but not the other way around.
|
||||
*/
|
||||
@Test
|
||||
public void testWildcardVersion() {
|
||||
|
||||
GuacamoleVersion specificVersion = new GuacamoleVersion("1.2.3-foo");
|
||||
GuacamoleVersion wildcardVersion = new GuacamoleVersion("*");
|
||||
|
||||
Assert.assertTrue(specificVersion.provides(wildcardVersion));
|
||||
Assert.assertFalse(wildcardVersion.provides(specificVersion));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user