diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectDirectory.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectDirectory.java index 37b07ba33..cec0432b2 100644 --- a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectDirectory.java +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectDirectory.java @@ -107,7 +107,7 @@ public class QuickConnectDirectory extends SimpleDirectory { String name = QCParser.getName(config); // Create a new connection and set the parent identifier. - Connection connection = new SimpleConnection(name, newConnectionId, config); + Connection connection = new SimpleConnection(name, newConnectionId, config, true); connection.setParentIdentifier(QuickConnectUserContext.ROOT_IDENTIFIER); // Place the object in this directory. diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connectable.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connectable.java index 39b12f98f..2f3326fdc 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connectable.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connectable.java @@ -19,6 +19,7 @@ package org.apache.guacamole.net.auth; +import java.util.Collections; import java.util.Map; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.GuacamoleTunnel; @@ -29,6 +30,46 @@ import org.apache.guacamole.protocol.GuacamoleClientInformation; */ public interface Connectable { + /* + * IMPORTANT: + * ---------- + * The web application (guacamole) defines its own version of this + * interface containing defaults which allow backwards compatibility with + * 1.0.0. Any changes to this interface MUST be properly reflected in that + * copy of the interface such that they are binary compatible. + */ + + /** + * Establishes a connection to guacd using the information associated with + * this object. The connection will be provided the given client + * information. + * + * @deprecated + * This function has been deprecated in favor of + * {@link #connect(org.apache.guacamole.protocol.GuacamoleClientInformation, java.util.Map)}, + * which allows for connection parameter tokens to be injected and + * applied by cooperating extensions, replacing the functionality + * previously provided through the {@link org.apache.guacamole.token.StandardTokens} + * class. It continues to be defined on this interface for + * compatibility. New implementations should instead implement + * {@link #connect(org.apache.guacamole.protocol.GuacamoleClientInformation, java.util.Map)}. + * + * @param info + * Information associated with the connecting client. + * + * @return + * A fully-established GuacamoleTunnel. + * + * @throws GuacamoleException + * If an error occurs while connecting to guacd, or if permission to + * connect is denied. + */ + @Deprecated + default GuacamoleTunnel connect(GuacamoleClientInformation info) + throws GuacamoleException { + return this.connect(info, Collections.emptyMap()); + } + /** * Establishes a connection to guacd using the information associated with * this object. The connection will be provided the given client diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnection.java index b80e8683a..95b6e9326 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnection.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnection.java @@ -19,6 +19,7 @@ package org.apache.guacamole.net.auth; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; @@ -39,6 +40,24 @@ public class DelegatingConnection implements Connection { */ private final Connection connection; + /** + * The tokens which should apply strictly to the next call to + * {@link #connect(org.apache.guacamole.protocol.GuacamoleClientInformation)}. + * This storage is intended as a temporary bridge allowing the old version + * of connect() to be overridden while still resulting in the same behavior + * as older versions of DelegatingConnection. This storage should be + * removed once support for the old, deprecated connect() is removed. + */ + private final ThreadLocal> currentTokens = + new ThreadLocal>() { + + @Override + protected Map initialValue() { + return Collections.emptyMap(); + } + + }; + /** * Wraps the given Connection such that all function calls against this * DelegatingConnection will be delegated to it. @@ -127,10 +146,27 @@ public class DelegatingConnection implements Connection { return connection.getSharingProfileIdentifiers(); } + @Override + @Deprecated + public GuacamoleTunnel connect(GuacamoleClientInformation info) + throws GuacamoleException { + return connection.connect(info, currentTokens.get()); + } + @Override public GuacamoleTunnel connect(GuacamoleClientInformation info, Map tokens) throws GuacamoleException { - return connection.connect(info, tokens); + + // Make received tokens available within the legacy connect() strictly + // in context of the current connect() call + try { + currentTokens.set(tokens); + return connect(info); + } + finally { + currentTokens.remove(); + } + } @Override diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnectionGroup.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnectionGroup.java index 1d958bdb2..5af6eb13e 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnectionGroup.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnectionGroup.java @@ -19,6 +19,7 @@ package org.apache.guacamole.net.auth; +import java.util.Collections; import java.util.Map; import java.util.Set; import org.apache.guacamole.GuacamoleException; @@ -36,6 +37,25 @@ public class DelegatingConnectionGroup implements ConnectionGroup { */ private final ConnectionGroup connectionGroup; + /** + * The tokens which should apply strictly to the next call to + * {@link #connect(org.apache.guacamole.protocol.GuacamoleClientInformation)}. + * This storage is intended as a temporary bridge allowing the old version + * of connect() to be overridden while still resulting in the same behavior + * as older versions of DelegatingConnectionGroup. This storage + * should be removed once support for the old, deprecated connect() is + * removed. + */ + private final ThreadLocal> currentTokens = + new ThreadLocal>() { + + @Override + protected Map initialValue() { + return Collections.emptyMap(); + } + + }; + /** * Wraps the given ConnectionGroup such that all function calls against this * DelegatingConnectionGroup will be delegated to it. @@ -118,10 +138,27 @@ public class DelegatingConnectionGroup implements ConnectionGroup { connectionGroup.setAttributes(attributes); } + @Override + @Deprecated + public GuacamoleTunnel connect(GuacamoleClientInformation info) + throws GuacamoleException { + return connectionGroup.connect(info, currentTokens.get()); + } + @Override public GuacamoleTunnel connect(GuacamoleClientInformation info, Map tokens) throws GuacamoleException { - return connectionGroup.connect(info, tokens); + + // Make received tokens available within the legacy connect() strictly + // in context of the current connect() call + try { + currentTokens.set(tokens); + return connect(info); + } + finally { + currentTokens.remove(); + } + } @Override diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java index 6d08d99ac..202181ab9 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java @@ -167,7 +167,7 @@ public abstract class SimpleAuthenticationProvider return null; // Return user context restricted to authorized configs - return new SimpleUserContext(this, authenticatedUser.getIdentifier(), configs); + return new SimpleUserContext(this, authenticatedUser.getIdentifier(), configs, true); } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java index 2dec26a94..2934cbe9f 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java @@ -41,10 +41,10 @@ import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.token.TokenFilter; /** - * An extremely basic Connection implementation. The underlying connection to - * guacd is established using the configuration information provided in - * guacamole.properties. Parameter tokens provided to connect() are - * automatically applied. Tracking of active connections and connection history + * A Connection implementation which establishes the underlying connection to + * guacd using the configuration information provided in guacamole.properties. + * Parameter tokens provided to connect() are automatically applied if + * explicitly requested. Tracking of active connections and connection history * is not provided. */ public class SimpleConnection extends AbstractConnection { @@ -52,36 +52,133 @@ public class SimpleConnection extends AbstractConnection { /** * Backing configuration, containing all sensitive information. */ - private GuacamoleConfiguration config; + private GuacamoleConfiguration fullConfig; /** - * Creates a completely uninitialized SimpleConnection. + * Whether parameter tokens in the underlying GuacamoleConfiguration should + * be automatically applied upon connecting. If false, parameter tokens + * will not be interpreted at all. + */ + private final boolean interpretTokens; + + /** + * The tokens which should apply strictly to the next call to + * {@link #connect(org.apache.guacamole.protocol.GuacamoleClientInformation)}. + * This storage is intended as a temporary bridge allowing the old version + * of connect() to be overridden while still resulting in the same behavior + * as older versions of SimpleConnection. This storage should be + * removed once support for the old, deprecated connect() is removed. + */ + private final ThreadLocal> currentTokens = + new ThreadLocal>() { + + @Override + protected Map initialValue() { + return Collections.emptyMap(); + } + + }; + + /** + * Creates a completely uninitialized SimpleConnection. The name, + * identifier, and configuration of this SimpleConnection must eventually + * be set before the SimpleConnection may be used. Parameter tokens within + * the GuacamoleConfiguration eventually supplied with + * {@link #setConfiguration(org.apache.guacamole.protocol.GuacamoleConfiguration)} + * will not be interpreted. */ public SimpleConnection() { + this(false); + } + + /** + * Creates a completely uninitialized SimpleConnection. The name, + * identifier, and configuration of this SimpleConnection must eventually + * be set before the SimpleConnection may be used. Parameter tokens within + * the GuacamoleConfiguration eventually supplied with + * {@link #setConfiguration(org.apache.guacamole.protocol.GuacamoleConfiguration)} + * will not be interpreted unless explicitly requested. + * + * @param interpretTokens + * Whether parameter tokens in the underlying GuacamoleConfiguration + * should be automatically applied upon connecting. If false, parameter + * tokens will not be interpreted at all. + */ + public SimpleConnection(boolean interpretTokens) { + this.interpretTokens = interpretTokens; } /** * Creates a new SimpleConnection having the given identifier and - * GuacamoleConfiguration. + * GuacamoleConfiguration. Parameter tokens within the + * GuacamoleConfiguration will not be interpreted unless explicitly + * requested. * - * @param name The name to associate with this connection. - * @param identifier The identifier to associate with this connection. - * @param config The configuration describing how to connect to this - * connection. + * @param name + * The name to associate with this connection. + * + * @param identifier + * The identifier to associate with this connection. + * + * @param config + * The configuration describing how to connect to this connection. */ public SimpleConnection(String name, String identifier, GuacamoleConfiguration config) { - - // Set name - setName(name); + this(name, identifier, config, false); + } - // Set identifier - setIdentifier(identifier); + /** + * Creates a new SimpleConnection having the given identifier and + * GuacamoleConfiguration. Parameter tokens will be interpreted if + * explicitly requested. + * + * @param name + * The name to associate with this connection. + * + * @param identifier + * The identifier to associate with this connection. + * + * @param config + * The configuration describing how to connect to this connection. + * + * @param interpretTokens + * Whether parameter tokens in the underlying GuacamoleConfiguration + * should be automatically applied upon connecting. If false, parameter + * tokens will not be interpreted at all. + */ + public SimpleConnection(String name, String identifier, + GuacamoleConfiguration config, boolean interpretTokens) { - // Set config - setConfiguration(config); - this.config = config; + super.setName(name); + super.setIdentifier(identifier); + super.setConfiguration(config); + this.fullConfig = config; + this.interpretTokens = interpretTokens; + + } + + /** + * Returns the GuacamoleConfiguration describing how to connect to this + * connection. Unlike {@link #getConfiguration()}, which is allowed to omit + * or tokenize information, the GuacamoleConfiguration returned by this + * function will always be the full configuration to be used to establish + * the connection, as provided when this SimpleConnection was created or via + * {@link #setConfiguration(org.apache.guacamole.protocol.GuacamoleConfiguration)}. + * + * @return + * The full GuacamoleConfiguration describing how to connect to this + * connection, without any information omitted or tokenized. + */ + protected GuacamoleConfiguration getFullConfiguration() { + return fullConfig; + } + + @Override + public void setConfiguration(GuacamoleConfiguration config) { + super.setConfiguration(config); + this.fullConfig = config; } @Override @@ -100,8 +197,9 @@ public class SimpleConnection extends AbstractConnection { } @Override - public GuacamoleTunnel connect(GuacamoleClientInformation info, - Map tokens) throws GuacamoleException { + @Deprecated + public GuacamoleTunnel connect(GuacamoleClientInformation info) + throws GuacamoleException { // Retrieve proxy configuration from environment Environment environment = new LocalEnvironment(); @@ -112,8 +210,8 @@ public class SimpleConnection extends AbstractConnection { int port = proxyConfig.getPort(); // Apply tokens to config parameters - GuacamoleConfiguration filteredConfig = new GuacamoleConfiguration(config); - new TokenFilter(tokens).filterValues(filteredConfig.getParameters()); + GuacamoleConfiguration filteredConfig = new GuacamoleConfiguration(getFullConfiguration()); + new TokenFilter(currentTokens.get()).filterValues(filteredConfig.getParameters()); GuacamoleSocket socket; @@ -143,6 +241,41 @@ public class SimpleConnection extends AbstractConnection { } return new SimpleGuacamoleTunnel(socket); + + } + + /** + * {@inheritDoc} + * + *

This implementation will connect using the GuacamoleConfiguration + * returned by {@link #getFullConfiguration()}, honoring the + * "guacd-hostname", "guacd-port", and "guacd-ssl" properties set within + * guacamole.properties. Parameter tokens will be taken into account if + * the SimpleConnection was explicitly requested to do so when created. + * + *

Implementations requiring more complex behavior should consider using + * the {@link AbstractConnection} base class or implementing + * {@link org.apache.guacamole.net.auth.Connection} directly. + */ + @Override + public GuacamoleTunnel connect(GuacamoleClientInformation info, + Map tokens) throws GuacamoleException { + + // Make received tokens available within the legacy connect() strictly + // in context of the current connect() call + try { + + // Automatically filter configurations only if explicitly + // configured to do so + if (interpretTokens) + currentTokens.set(tokens); + + return connect(info); + + } + finally { + currentTokens.remove(); + } } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnectionGroup.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnectionGroup.java index a077eb312..b2f7de0ca 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnectionGroup.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnectionGroup.java @@ -47,7 +47,7 @@ public class SimpleConnectionGroup extends AbstractConnectionGroup { * The identifiers of all connection groups in this group. */ private final Set connectionGroupIdentifiers; - + /** * Creates a new SimpleConnectionGroup having the given name and identifier * which will expose the given contents. @@ -109,9 +109,16 @@ public class SimpleConnectionGroup extends AbstractConnectionGroup { } @Override - public GuacamoleTunnel connect(GuacamoleClientInformation info, - Map tokens) throws GuacamoleException { + @Deprecated + public GuacamoleTunnel connect(GuacamoleClientInformation info) + throws GuacamoleException { throw new GuacamoleSecurityException("Permission denied."); } + @Override + public GuacamoleTunnel connect(GuacamoleClientInformation info, + Map tokens) throws GuacamoleException { + return connect(info); + } + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java index 03e94fbac..2cefee2e7 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java @@ -59,7 +59,8 @@ public class SimpleUserContext extends AbstractUserContext { * Creates a new SimpleUserContext which provides access to only those * configurations within the given Map. The username is set to the * ANONYMOUS_IDENTIFIER defined by AuthenticatedUser, effectively declaring - * the current user as anonymous. + * the current user as anonymous. Parameter tokens within the given + * GuacamoleConfigurations will not be interpreted. * * @param authProvider * The AuthenticationProvider creating this UserContext. @@ -76,6 +77,8 @@ public class SimpleUserContext extends AbstractUserContext { /** * Creates a new SimpleUserContext for the user with the given username * which provides access to only those configurations within the given Map. + * Parameter tokens within the given GuacamoleConfigurations will not be + * interpreted. * * @param authProvider * The AuthenticationProvider creating this UserContext. @@ -89,6 +92,33 @@ public class SimpleUserContext extends AbstractUserContext { */ public SimpleUserContext(AuthenticationProvider authProvider, String username, Map configs) { + this(authProvider, username, configs, false); + } + + /** + * Creates a new SimpleUserContext for the user with the given username + * which provides access to only those configurations within the given Map. + * Parameter tokens within the given GuacamoleConfigurations will be + * interpreted if explicitly requested. + * + * @param authProvider + * The AuthenticationProvider creating this UserContext. + * + * @param username + * The username of the user associated with this UserContext. + * + * @param configs + * A Map of all configurations for which the user associated with + * this UserContext has read access. + * + * @param interpretTokens + * Whether parameter tokens in the underlying GuacamoleConfigurations + * should be automatically applied upon connecting. If false, parameter + * tokens will not be interpreted at all. + */ + public SimpleUserContext(AuthenticationProvider authProvider, + String username, Map configs, + boolean interpretTokens) { // Produce map of connections from given configs Map connections = new ConcurrentHashMap(configs.size()); @@ -99,7 +129,7 @@ public class SimpleUserContext extends AbstractUserContext { GuacamoleConfiguration config = configEntry.getValue(); // Add as simple connection - Connection connection = new SimpleConnection(identifier, identifier, config); + Connection connection = new SimpleConnection(identifier, identifier, config, interpretTokens); connection.setParentIdentifier(DEFAULT_ROOT_CONNECTION_GROUP); connections.put(identifier, connection); diff --git a/guacamole/src/main/java/org/apache/guacamole/net/auth/Connectable.java b/guacamole/src/main/java/org/apache/guacamole/net/auth/Connectable.java new file mode 100644 index 000000000..e09baa1d8 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/net/auth/Connectable.java @@ -0,0 +1,109 @@ +/* + * 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.net.auth; + +import java.util.Collections; +import java.util.Map; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.GuacamoleTunnel; +import org.apache.guacamole.protocol.GuacamoleClientInformation; + +/** + * Internal, ABI-compatible version of the Connectable interface from + * guacamole-ext which defines fallback defaults for older versions of the API. + * As this interface will take precedence in the servlet container's + * classloader over the definition from guacamole-ext, this allows backwards + * compatibility with the 1.0.0 API while keeping the actual API definition + * within guacamole-ext strict. + * + *

For this to work, this interface definition MUST be 100% + * ABI-compatible with the Connectable interface defined by guacamole-ext in + * 1.0.0 and onward. + */ +public interface Connectable { + + /** + * Establishes a connection to guacd using the information associated with + * this object. The connection will be provided the given client + * information. + * + *

This definition is the legacy connect() definition from 1.0.0 and + * older. It is redefined here for the sake of ABI compatibility with + * 1.0.0 but is deprecated within guacamole-ext. + * + * @deprecated + * This definition exists solely for binary compatibility. It should + * never be used by new code. New implementations should instead use + * the current version of connect() as defined by guacamole-ext. + * + * @param info + * Information associated with the connecting client. + * + * @return + * A fully-established GuacamoleTunnel. + * + * @throws GuacamoleException + * If an error occurs while connecting to guacd, or if permission to + * connect is denied. + */ + @Deprecated + default GuacamoleTunnel connect(GuacamoleClientInformation info) + throws GuacamoleException { + + // Pass through usages of the old API to the new API + return this.connect(info, Collections.emptyMap()); + + } + + /** + * {@inheritDoc} + * + *

This definition is the current version of connect() as defined by + * guacamole-ext. + * + *

A default implementation which invokes the old, deprecated + * {@link #connect(org.apache.guacamole.protocol.GuacamoleClientInformation)} + * is provided solely for compatibility with extensions which implement only + * the old version of this function. This default implementation is useful + * only for extensions relying on the older API and will be removed when + * support for that version of the API is removed. + * + * @see + * The definition of getActiveConnections() in the current version of + * the Connectable interface, as defined by guacamole-ext. + */ + default GuacamoleTunnel connect(GuacamoleClientInformation info, + Map tokens) throws GuacamoleException { + + // Allow old implementations of Connectable to continue to work + return this.connect(info); + + } + + /** + * {@inheritDoc} + * + * @see + * The definition of getActiveConnections() in the current version of + * the Connectable interface, as defined by guacamole-ext. + */ + int getActiveConnections(); + +}