GUACAMOLE-524: Deprecate and replace StandardTokens with arbitrary tokens provided to Connectable.connect().

This commit is contained in:
Michael Jumper
2018-10-04 00:41:07 -07:00
parent 3089e71e60
commit 1210d5624c
22 changed files with 268 additions and 154 deletions

View File

@@ -499,6 +499,10 @@ public class ConnectionService extends ModeledChildDirectoryObjectService<Modele
* @param info * @param info
* Information associated with the connecting client. * Information associated with the connecting client.
* *
* @param tokens
* A Map containing the token names and corresponding values to be
* applied as parameter tokens when establishing the connection.
*
* @return * @return
* A connected GuacamoleTunnel associated with a newly-established * A connected GuacamoleTunnel associated with a newly-established
* connection. * connection.
@@ -507,12 +511,12 @@ public class ConnectionService extends ModeledChildDirectoryObjectService<Modele
* If permission to connect to this connection is denied. * If permission to connect to this connection is denied.
*/ */
public GuacamoleTunnel connect(ModeledAuthenticatedUser user, public GuacamoleTunnel connect(ModeledAuthenticatedUser user,
ModeledConnection connection, GuacamoleClientInformation info) ModeledConnection connection, GuacamoleClientInformation info,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
// Connect only if READ permission is granted // Connect only if READ permission is granted
if (hasObjectPermission(user, connection.getIdentifier(), ObjectPermission.Type.READ)) if (hasObjectPermission(user, connection.getIdentifier(), ObjectPermission.Type.READ))
return tunnelService.getGuacamoleTunnel(user, connection, info); return tunnelService.getGuacamoleTunnel(user, connection, info, tokens);
// The user does not have permission to connect // The user does not have permission to connect
throw new GuacamoleSecurityException("Permission denied."); throw new GuacamoleSecurityException("Permission denied.");

View File

@@ -259,8 +259,9 @@ public class ModeledConnection extends ModeledChildDirectoryObject<ConnectionMod
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { public GuacamoleTunnel connect(GuacamoleClientInformation info,
return connectionService.connect(getCurrentUser(), this, info); Map<String, String> tokens) throws GuacamoleException {
return connectionService.connect(getCurrentUser(), this, info, tokens);
} }
@Override @Override

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.jdbc.connectiongroup;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper;
@@ -243,6 +244,10 @@ public class ConnectionGroupService extends ModeledChildDirectoryObjectService<M
* @param info * @param info
* Information associated with the connecting client. * Information associated with the connecting client.
* *
* @param tokens
* A Map containing the token names and corresponding values to be
* applied as parameter tokens when establishing the connection.
*
* @return * @return
* A connected GuacamoleTunnel associated with a newly-established * A connected GuacamoleTunnel associated with a newly-established
* connection. * connection.
@@ -251,12 +256,12 @@ public class ConnectionGroupService extends ModeledChildDirectoryObjectService<M
* If permission to connect to this connection is denied. * If permission to connect to this connection is denied.
*/ */
public GuacamoleTunnel connect(ModeledAuthenticatedUser user, public GuacamoleTunnel connect(ModeledAuthenticatedUser user,
ModeledConnectionGroup connectionGroup, GuacamoleClientInformation info) ModeledConnectionGroup connectionGroup, GuacamoleClientInformation info,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
// Connect only if READ permission is granted // Connect only if READ permission is granted
if (hasObjectPermission(user, connectionGroup.getIdentifier(), ObjectPermission.Type.READ)) if (hasObjectPermission(user, connectionGroup.getIdentifier(), ObjectPermission.Type.READ))
return tunnelService.getGuacamoleTunnel(user, connectionGroup, info); return tunnelService.getGuacamoleTunnel(user, connectionGroup, info, tokens);
// The user does not have permission to connect // The user does not have permission to connect
throw new GuacamoleSecurityException("Permission denied."); throw new GuacamoleSecurityException("Permission denied.");

View File

@@ -135,9 +135,9 @@ public class ModeledConnectionGroup extends ModeledChildDirectoryObject<Connecti
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) public GuacamoleTunnel connect(GuacamoleClientInformation info,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
return connectionGroupService.connect(getCurrentUser(), this, info); return connectionGroupService.connect(getCurrentUser(), this, info, tokens);
} }
@Override @Override

View File

@@ -122,8 +122,8 @@ public class RootConnectionGroup extends RestrictedObject
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) public GuacamoleTunnel connect(GuacamoleClientInformation info,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied."); throw new GuacamoleSecurityException("Permission denied.");
} }

View File

@@ -131,9 +131,9 @@ public class SharedConnection implements Connection {
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) public GuacamoleTunnel connect(GuacamoleClientInformation info,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
return tunnelService.getGuacamoleTunnel(user, definition, info); return tunnelService.getGuacamoleTunnel(user, definition, info, tokens);
} }
@Override @Override

View File

@@ -98,8 +98,8 @@ public class SharedRootConnectionGroup implements ConnectionGroup {
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) public GuacamoleTunnel connect(GuacamoleClientInformation info,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied."); throw new GuacamoleSecurityException("Permission denied.");
} }

View File

@@ -52,7 +52,6 @@ import org.apache.guacamole.net.auth.ConnectionGroup;
import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket; import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket;
import org.apache.guacamole.protocol.GuacamoleClientInformation; import org.apache.guacamole.protocol.GuacamoleClientInformation;
import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.apache.guacamole.token.StandardTokens;
import org.apache.guacamole.token.TokenFilter; import org.apache.guacamole.token.TokenFilter;
import org.mybatis.guice.transactional.Transactional; import org.mybatis.guice.transactional.Transactional;
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper; import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper;
@@ -233,13 +232,6 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
for (ConnectionParameterModel parameter : parameters) for (ConnectionParameterModel parameter : parameters)
config.setParameter(parameter.getName(), parameter.getValue()); config.setParameter(parameter.getName(), parameter.getValue());
// Build token filter containing credential tokens
TokenFilter tokenFilter = new TokenFilter();
StandardTokens.addStandardTokens(tokenFilter, user);
// Filter the configuration
tokenFilter.filterValues(config.getParameters());
return config; return config;
} }
@@ -279,13 +271,6 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
for (SharingProfileParameterModel parameter : parameters) for (SharingProfileParameterModel parameter : parameters)
config.setParameter(parameter.getName(), parameter.getValue()); config.setParameter(parameter.getName(), parameter.getValue());
// Build token filter containing credential tokens
TokenFilter tokenFilter = new TokenFilter();
StandardTokens.addStandardTokens(tokenFilter, user);
// Filter the configuration
tokenFilter.filterValues(config.getParameters());
return config; return config;
} }
@@ -454,6 +439,10 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
* Information describing the Guacamole client connecting to the given * Information describing the Guacamole client connecting to the given
* connection. * connection.
* *
* @param tokens
* A Map containing the token names and corresponding values to be
* applied as parameter tokens when establishing the connection.
*
* @param interceptErrors * @param interceptErrors
* Whether errors from the upstream remote desktop should be * Whether errors from the upstream remote desktop should be
* intercepted and rethrown as GuacamoleUpstreamExceptions. * intercepted and rethrown as GuacamoleUpstreamExceptions.
@@ -467,7 +456,8 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
* while connection configuration information is being retrieved. * while connection configuration information is being retrieved.
*/ */
private GuacamoleTunnel assignGuacamoleTunnel(ActiveConnectionRecord activeConnection, private GuacamoleTunnel assignGuacamoleTunnel(ActiveConnectionRecord activeConnection,
GuacamoleClientInformation info, boolean interceptErrors) throws GuacamoleException { GuacamoleClientInformation info, Map<String, String> tokens,
boolean interceptErrors) throws GuacamoleException {
// Record new active connection // Record new active connection
Runnable cleanupTask = new ConnectionCleanupTask(activeConnection); Runnable cleanupTask = new ConnectionCleanupTask(activeConnection);
@@ -504,6 +494,13 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
} }
// Build token filter containing credential tokens
TokenFilter tokenFilter = new TokenFilter();
tokenFilter.setTokens(tokens);
// Filter the configuration
tokenFilter.filterValues(config.getParameters());
// Obtain socket which will automatically run the cleanup task // Obtain socket which will automatically run the cleanup task
ConfiguredGuacamoleSocket socket = new ConfiguredGuacamoleSocket( ConfiguredGuacamoleSocket socket = new ConfiguredGuacamoleSocket(
getUnconfiguredGuacamoleSocket(connection.getGuacamoleProxyConfiguration(), getUnconfiguredGuacamoleSocket(connection.getGuacamoleProxyConfiguration(),
@@ -651,8 +648,8 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
@Override @Override
@Transactional @Transactional
public GuacamoleTunnel getGuacamoleTunnel(final ModeledAuthenticatedUser user, public GuacamoleTunnel getGuacamoleTunnel(final ModeledAuthenticatedUser user,
final ModeledConnection connection, GuacamoleClientInformation info) final ModeledConnection connection, GuacamoleClientInformation info,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
// Acquire access to single connection, ignoring the failover-only flag // Acquire access to single connection, ignoring the failover-only flag
acquire(user, Collections.singletonList(connection), true); acquire(user, Collections.singletonList(connection), true);
@@ -660,7 +657,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
// Connect only if the connection was successfully acquired // Connect only if the connection was successfully acquired
ActiveConnectionRecord connectionRecord = activeConnectionRecordProvider.get(); ActiveConnectionRecord connectionRecord = activeConnectionRecordProvider.get();
connectionRecord.init(user, connection); connectionRecord.init(user, connection);
return assignGuacamoleTunnel(connectionRecord, info, false); return assignGuacamoleTunnel(connectionRecord, info, tokens, false);
} }
@@ -673,7 +670,8 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
@Transactional @Transactional
public GuacamoleTunnel getGuacamoleTunnel(ModeledAuthenticatedUser user, public GuacamoleTunnel getGuacamoleTunnel(ModeledAuthenticatedUser user,
ModeledConnectionGroup connectionGroup, ModeledConnectionGroup connectionGroup,
GuacamoleClientInformation info) throws GuacamoleException { GuacamoleClientInformation info, Map<String, String> tokens)
throws GuacamoleException {
// Track failures in upstream (remote desktop) connections // Track failures in upstream (remote desktop) connections
boolean upstreamHasFailed = false; boolean upstreamHasFailed = false;
@@ -706,7 +704,8 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
// Connect to acquired child // Connect to acquired child
ActiveConnectionRecord connectionRecord = activeConnectionRecordProvider.get(); ActiveConnectionRecord connectionRecord = activeConnectionRecordProvider.get();
connectionRecord.init(user, connectionGroup, connection); connectionRecord.init(user, connectionGroup, connection);
GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord, info, connections.size() > 1); GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord,
info, tokens, connections.size() > 1);
// If session affinity is enabled, prefer this connection going forward // If session affinity is enabled, prefer this connection going forward
if (connectionGroup.isSessionAffinityEnabled()) if (connectionGroup.isSessionAffinityEnabled())
@@ -755,7 +754,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
@Transactional @Transactional
public GuacamoleTunnel getGuacamoleTunnel(RemoteAuthenticatedUser user, public GuacamoleTunnel getGuacamoleTunnel(RemoteAuthenticatedUser user,
SharedConnectionDefinition definition, SharedConnectionDefinition definition,
GuacamoleClientInformation info) GuacamoleClientInformation info, Map<String, String> tokens)
throws GuacamoleException { throws GuacamoleException {
// Create a connection record which describes the shared connection // Create a connection record which describes the shared connection
@@ -764,7 +763,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
definition.getSharingProfile()); definition.getSharingProfile());
// Connect to shared connection described by the created record // Connect to shared connection described by the created record
GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord, info, false); GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord, info, tokens, false);
// Register tunnel, such that it is closed when the // Register tunnel, such that it is closed when the
// SharedConnectionDefinition is invalidated // SharedConnectionDefinition is invalidated

View File

@@ -20,6 +20,7 @@
package org.apache.guacamole.auth.jdbc.tunnel; package org.apache.guacamole.auth.jdbc.tunnel;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
@@ -73,6 +74,10 @@ public interface GuacamoleTunnelService {
* Information describing the Guacamole client connecting to the given * Information describing the Guacamole client connecting to the given
* connection. * connection.
* *
* @param tokens
* A Map containing the token names and corresponding values to be
* applied as parameter tokens when establishing the connection.
*
* @return * @return
* A new GuacamoleTunnel which is configured and connected to the given * A new GuacamoleTunnel which is configured and connected to the given
* connection. * connection.
@@ -82,8 +87,8 @@ public interface GuacamoleTunnelService {
* rules. * rules.
*/ */
GuacamoleTunnel getGuacamoleTunnel(ModeledAuthenticatedUser user, GuacamoleTunnel getGuacamoleTunnel(ModeledAuthenticatedUser user,
ModeledConnection connection, GuacamoleClientInformation info) ModeledConnection connection, GuacamoleClientInformation info,
throws GuacamoleException; Map<String, String> tokens) throws GuacamoleException;
/** /**
* Returns a collection containing connection records representing all * Returns a collection containing connection records representing all
@@ -117,6 +122,10 @@ public interface GuacamoleTunnelService {
* Information describing the Guacamole client connecting to the given * Information describing the Guacamole client connecting to the given
* connection group. * connection group.
* *
* @param tokens
* A Map containing the token names and corresponding values to be
* applied as parameter tokens when establishing the connection.
*
* @return * @return
* A new GuacamoleTunnel which is configured and connected to the given * A new GuacamoleTunnel which is configured and connected to the given
* connection group. * connection group.
@@ -127,7 +136,7 @@ public interface GuacamoleTunnelService {
*/ */
GuacamoleTunnel getGuacamoleTunnel(ModeledAuthenticatedUser user, GuacamoleTunnel getGuacamoleTunnel(ModeledAuthenticatedUser user,
ModeledConnectionGroup connectionGroup, ModeledConnectionGroup connectionGroup,
GuacamoleClientInformation info) GuacamoleClientInformation info, Map<String, String> tokens)
throws GuacamoleException; throws GuacamoleException;
/** /**
@@ -163,6 +172,10 @@ public interface GuacamoleTunnelService {
* Information describing the Guacamole client connecting to the given * Information describing the Guacamole client connecting to the given
* connection. * connection.
* *
* @param tokens
* A Map containing the token names and corresponding values to be
* applied as parameter tokens when establishing the connection.
*
* @return * @return
* A new GuacamoleTunnel which is configured and connected to the given * A new GuacamoleTunnel which is configured and connected to the given
* active connection. * active connection.
@@ -173,7 +186,7 @@ public interface GuacamoleTunnelService {
*/ */
GuacamoleTunnel getGuacamoleTunnel(RemoteAuthenticatedUser user, GuacamoleTunnel getGuacamoleTunnel(RemoteAuthenticatedUser user,
SharedConnectionDefinition definition, SharedConnectionDefinition definition,
GuacamoleClientInformation info) GuacamoleClientInformation info, Map<String, String> tokens)
throws GuacamoleException; throws GuacamoleException;
} }

View File

@@ -39,8 +39,6 @@ import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.simple.SimpleConnection; import org.apache.guacamole.net.auth.simple.SimpleConnection;
import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.apache.guacamole.token.StandardTokens;
import org.apache.guacamole.token.TokenFilter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -122,10 +120,6 @@ public class ConnectionService {
confService.getLDAPSearchConstraints() confService.getLDAPSearchConstraints()
); );
// Build token filter containing credential tokens
TokenFilter tokenFilter = new TokenFilter();
StandardTokens.addStandardTokens(tokenFilter, user);
// Produce connections for each readable configuration // Produce connections for each readable configuration
Map<String, Connection> connections = new HashMap<String, Connection>(); Map<String, Connection> connections = new HashMap<String, Connection>();
while (results.hasMore()) { while (results.hasMore()) {
@@ -180,9 +174,6 @@ public class ConnectionService {
} }
// Filter the configuration, substituting all defined tokens
tokenFilter.filterValues(config.getParameters());
// Store connection using cn for both identifier and name // Store connection using cn for both identifier and name
String name = cn.getStringValue(); String name = cn.getStringValue();
Connection connection = new SimpleConnection(name, name, config); Connection connection = new SimpleConnection(name, name, config);

View File

@@ -108,8 +108,8 @@ public class QuickConnectionGroup extends AbstractConnectionGroup {
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) public GuacamoleTunnel connect(GuacamoleClientInformation info,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
// This group does not support connections // This group does not support connections
throw new GuacamoleSecurityException("Permission denied."); throw new GuacamoleSecurityException("Permission denied.");
} }

View File

@@ -19,6 +19,7 @@
package org.apache.guacamole.net.auth; package org.apache.guacamole.net.auth;
import java.util.Map;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.protocol.GuacamoleClientInformation; import org.apache.guacamole.protocol.GuacamoleClientInformation;
@@ -31,11 +32,21 @@ public interface Connectable {
/** /**
* Establishes a connection to guacd using the information associated with * Establishes a connection to guacd using the information associated with
* this object. The connection will be provided the given client * this object. The connection will be provided the given client
* information. * information. Implementations which support parameter tokens should
* apply the given tokens when configuring the connection, such as with a
* {@link org.apache.guacamole.token.TokenFilter}.
*
* @see <a href="http://guacamole.apache.org/doc/gug/configuring-guacamole.html#parameter-tokens">Parameter Tokens</a>
* *
* @param info * @param info
* Information associated with the connecting client. * Information associated with the connecting client.
* *
* @param tokens
* A Map containing the token names and corresponding values to be
* applied as parameter tokens when establishing the connection. If the
* implementation does not support parameter tokens, this Map may be
* ignored.
*
* @return * @return
* A fully-established GuacamoleTunnel. * A fully-established GuacamoleTunnel.
* *
@@ -43,8 +54,8 @@ public interface Connectable {
* If an error occurs while connecting to guacd, or if permission to * If an error occurs while connecting to guacd, or if permission to
* connect is denied. * connect is denied.
*/ */
public GuacamoleTunnel connect(GuacamoleClientInformation info) public GuacamoleTunnel connect(GuacamoleClientInformation info,
throws GuacamoleException; Map<String, String> tokens) throws GuacamoleException;
/** /**
* Returns the number of active connections associated with this object. * Returns the number of active connections associated with this object.

View File

@@ -128,9 +128,9 @@ public class DelegatingConnection implements Connection {
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) public GuacamoleTunnel connect(GuacamoleClientInformation info,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
return connection.connect(info); return connection.connect(info, tokens);
} }
@Override @Override

View File

@@ -119,8 +119,9 @@ public class DelegatingConnectionGroup implements ConnectionGroup {
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { public GuacamoleTunnel connect(GuacamoleClientInformation info,
return connectionGroup.connect(info); Map<String, String> tokens) throws GuacamoleException {
return connectionGroup.connect(info, tokens);
} }
@Override @Override

View File

@@ -31,8 +31,6 @@ import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.apache.guacamole.token.StandardTokens;
import org.apache.guacamole.token.TokenFilter;
/** /**
* Provides means of retrieving a set of named GuacamoleConfigurations for a * Provides means of retrieving a set of named GuacamoleConfigurations for a
@@ -140,84 +138,13 @@ public abstract class SimpleAuthenticationProvider
} }
/**
* Given an arbitrary credentials object, returns a Map containing all
* configurations authorized by those credentials, filtering those
* configurations using a TokenFilter and the standard credential tokens
* (like ${GUAC_USERNAME} and ${GUAC_PASSWORD}). The keys of this Map
* are Strings which uniquely identify each configuration.
*
* @param credentials
* The credentials to use to retrieve authorized configurations.
*
* @return
* A Map of all configurations authorized by the given credentials, or
* null if the credentials given are not authorized.
*
* @throws GuacamoleException
* If an error occurs while retrieving configurations.
*/
private Map<String, GuacamoleConfiguration>
getFilteredAuthorizedConfigurations(Credentials credentials)
throws GuacamoleException {
// Get configurations
Map<String, GuacamoleConfiguration> configs =
getAuthorizedConfigurations(credentials);
// Return as unauthorized if not authorized to retrieve configs
if (configs == null)
return null;
// Build credential TokenFilter
TokenFilter tokenFilter = new TokenFilter();
StandardTokens.addStandardTokens(tokenFilter, credentials);
// Filter each configuration
for (GuacamoleConfiguration config : configs.values())
tokenFilter.filterValues(config.getParameters());
return configs;
}
/**
* Given a user who has already been authenticated, returns a Map
* containing all configurations for which that user is authorized,
* filtering those configurations using a TokenFilter and the standard
* credential tokens (like ${GUAC_USERNAME} and ${GUAC_PASSWORD}). The keys
* of this Map are Strings which uniquely identify each configuration.
*
* @param authenticatedUser
* The user whose authorized configurations are to be retrieved.
*
* @return
* A Map of all configurations authorized for use by the given user, or
* null if the user is not authorized to use any configurations.
*
* @throws GuacamoleException
* If an error occurs while retrieving configurations.
*/
private Map<String, GuacamoleConfiguration>
getFilteredAuthorizedConfigurations(AuthenticatedUser authenticatedUser)
throws GuacamoleException {
// Pull cached configurations, if any
if (authenticatedUser instanceof SimpleAuthenticatedUser && authenticatedUser.getAuthenticationProvider() == this)
return ((SimpleAuthenticatedUser) authenticatedUser).getAuthorizedConfigurations();
// Otherwise, pull using credentials
return getFilteredAuthorizedConfigurations(authenticatedUser.getCredentials());
}
@Override @Override
public AuthenticatedUser authenticateUser(final Credentials credentials) public AuthenticatedUser authenticateUser(final Credentials credentials)
throws GuacamoleException { throws GuacamoleException {
// Get configurations // Get configurations
Map<String, GuacamoleConfiguration> configs = Map<String, GuacamoleConfiguration> configs =
getFilteredAuthorizedConfigurations(credentials); getAuthorizedConfigurations(credentials);
// Return as unauthorized if not authorized to retrieve configs // Return as unauthorized if not authorized to retrieve configs
if (configs == null) if (configs == null)
@@ -233,7 +160,7 @@ public abstract class SimpleAuthenticationProvider
// Get configurations // Get configurations
Map<String, GuacamoleConfiguration> configs = Map<String, GuacamoleConfiguration> configs =
getFilteredAuthorizedConfigurations(authenticatedUser); getAuthorizedConfigurations(authenticatedUser.getCredentials());
// Return as unauthorized if not authorized to retrieve configs // Return as unauthorized if not authorized to retrieve configs
if (configs == null) if (configs == null)

View File

@@ -38,9 +38,14 @@ import org.apache.guacamole.net.auth.GuacamoleProxyConfiguration;
import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket; import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket;
import org.apache.guacamole.protocol.GuacamoleClientInformation; import org.apache.guacamole.protocol.GuacamoleClientInformation;
import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.apache.guacamole.token.TokenFilter;
/** /**
* An extremely basic Connection implementation. * 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
* is not provided.
*/ */
public class SimpleConnection extends AbstractConnection { public class SimpleConnection extends AbstractConnection {
@@ -95,8 +100,8 @@ public class SimpleConnection extends AbstractConnection {
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) public GuacamoleTunnel connect(GuacamoleClientInformation info,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
// Retrieve proxy configuration from environment // Retrieve proxy configuration from environment
Environment environment = new LocalEnvironment(); Environment environment = new LocalEnvironment();
@@ -106,6 +111,11 @@ public class SimpleConnection extends AbstractConnection {
String hostname = proxyConfig.getHostname(); String hostname = proxyConfig.getHostname();
int port = proxyConfig.getPort(); int port = proxyConfig.getPort();
// Apply tokens to config parameters
GuacamoleConfiguration filteredConfig = new GuacamoleConfiguration(config);
TokenFilter tokenFilter = new TokenFilter();
tokenFilter.filterValues(config.getParameters());
GuacamoleSocket socket; GuacamoleSocket socket;
// Determine socket type based on required encryption method // Determine socket type based on required encryption method
@@ -115,7 +125,7 @@ public class SimpleConnection extends AbstractConnection {
case SSL: case SSL:
socket = new ConfiguredGuacamoleSocket( socket = new ConfiguredGuacamoleSocket(
new SSLGuacamoleSocket(hostname, port), new SSLGuacamoleSocket(hostname, port),
config, info filteredConfig, info
); );
break; break;
@@ -123,7 +133,7 @@ public class SimpleConnection extends AbstractConnection {
case NONE: case NONE:
socket = new ConfiguredGuacamoleSocket( socket = new ConfiguredGuacamoleSocket(
new InetGuacamoleSocket(hostname, port), new InetGuacamoleSocket(hostname, port),
config, info filteredConfig, info
); );
break; break;

View File

@@ -109,8 +109,8 @@ public class SimpleConnectionGroup extends AbstractConnectionGroup {
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) public GuacamoleTunnel connect(GuacamoleClientInformation info,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied."); throw new GuacamoleSecurityException("Permission denied.");
} }

View File

@@ -29,7 +29,12 @@ import org.apache.guacamole.net.auth.Credentials;
/** /**
* Utility class which provides access to standardized token names, as well as * Utility class which provides access to standardized token names, as well as
* facilities for generating those tokens from common objects. * facilities for generating those tokens from common objects.
*
* @deprecated Standard tokens are now supplied by default to the connect()
* functions of connections and connection groups. Manually generating the
* standard tokens is not necessary.
*/ */
@Deprecated
public class StandardTokens { public class StandardTokens {
/** /**

View File

@@ -128,7 +128,8 @@ public class APIConnectionWrapper implements Connection {
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { public GuacamoleTunnel connect(GuacamoleClientInformation info,
Map<String, String> tokens) throws GuacamoleException {
throw new UnsupportedOperationException("Operation not supported."); throw new UnsupportedOperationException("Operation not supported.");
} }

View File

@@ -112,7 +112,8 @@ public class APIConnectionGroupWrapper implements ConnectionGroup {
} }
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { public GuacamoleTunnel connect(GuacamoleClientInformation info,
Map<String, String> tokens) throws GuacamoleException {
throw new UnsupportedOperationException("Operation not supported."); throw new UnsupportedOperationException("Operation not supported.");
} }

View File

@@ -0,0 +1,139 @@
/*
* 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.tunnel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials;
/**
* Map which is automatically populated with the name/value pairs of all
* standardized tokens available for a particular AuthenticatedUser.
*/
public class StandardTokenMap extends HashMap<String, String> {
/**
* The name of the token containing the user's username.
*/
public static final String USERNAME_TOKEN = "GUAC_USERNAME";
/**
* The name of the token containing the user's password.
*/
public static final String PASSWORD_TOKEN = "GUAC_PASSWORD";
/**
* The name of the token containing the hostname/address of the machine the
* user authenticated from.
*/
public static final String CLIENT_HOSTNAME_TOKEN = "GUAC_CLIENT_HOSTNAME";
/**
* The name of the token containing the IP address of the machine the user
* authenticated from.
*/
public static final String CLIENT_ADDRESS_TOKEN = "GUAC_CLIENT_ADDRESS";
/**
* The name of the token containing the current date (server-local time).
*/
public static final String DATE_TOKEN = "GUAC_DATE";
/**
* The name of the token containing the current time (server-local time).
*/
public static final String TIME_TOKEN = "GUAC_TIME";
/**
* The date format that should be used for the date token. This format must
* be compatible with Java's SimpleDateFormat.
*/
private static final String DATE_FORMAT = "yyyyMMdd";
/**
* The date format that should be used for the time token. This format must
* be compatible with Java's SimpleDateFormat.
*/
private static final String TIME_FORMAT = "HHmmss";
/**
* The prefix of the arbitrary attribute tokens.
*/
public static final String ATTR_TOKEN_PREFIX = "GUAC_ATTR_";
/**
* Creates a new StandardTokenMap which is pre-populated with the
* name/value pairs of all standardized tokens available for the given
* AuthenticatedUser.
*
* @param authenticatedUser
* The AuthenticatedUser to generate standard tokens for.
*/
public StandardTokenMap(AuthenticatedUser authenticatedUser) {
// Add date/time tokens (server-local time)
Date currentTime = new Date();
put(DATE_TOKEN, new SimpleDateFormat(DATE_FORMAT).format(currentTime));
put(TIME_TOKEN, new SimpleDateFormat(TIME_FORMAT).format(currentTime));
Credentials credentials = authenticatedUser.getCredentials();
Map<String, String> attributes = authenticatedUser.getAttributes();
// Add username token
String username = credentials.getUsername();
if (username != null)
put(USERNAME_TOKEN, username);
// Default to the authenticated user's username for the GUAC_USERNAME
// token
else
put(USERNAME_TOKEN, authenticatedUser.getIdentifier());
// Add password token
String password = credentials.getPassword();
if (password != null)
put(PASSWORD_TOKEN, password);
// Add client hostname token
String hostname = credentials.getRemoteHostname();
if (hostname != null)
put(CLIENT_HOSTNAME_TOKEN, hostname);
// Add client address token
String address = credentials.getRemoteAddress();
if (address != null)
put(CLIENT_ADDRESS_TOKEN, address);
// Add tokens for all attributes on the AuthenticatedUser
if (attributes != null) {
for (Map.Entry entry : attributes.entrySet()) {
String key = entry.getKey().toString();
String tokenName = ATTR_TOKEN_PREFIX + key.toUpperCase();
String tokenValue = entry.getValue().toString();
put(tokenName, tokenValue);
}
}
}
}

View File

@@ -22,6 +22,7 @@ package org.apache.guacamole.tunnel;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import java.util.List; import java.util.List;
import java.util.Map;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.GuacamoleSession; import org.apache.guacamole.GuacamoleSession;
@@ -187,6 +188,10 @@ public class TunnelRequestService {
* @param info * @param info
* Information describing the connected Guacamole client. * Information describing the connected Guacamole client.
* *
* @param tokens
* A Map containing the token names and corresponding values to be
* applied as parameter tokens when establishing the connection.
*
* @return * @return
* A new tunnel, connected as required by the request. * A new tunnel, connected as required by the request.
* *
@@ -195,7 +200,7 @@ public class TunnelRequestService {
*/ */
protected GuacamoleTunnel createConnectedTunnel(UserContext context, protected GuacamoleTunnel createConnectedTunnel(UserContext context,
final TunnelRequest.Type type, String id, final TunnelRequest.Type type, String id,
GuacamoleClientInformation info) GuacamoleClientInformation info, Map<String, String> tokens)
throws GuacamoleException { throws GuacamoleException {
// Create connected tunnel from identifier // Create connected tunnel from identifier
@@ -216,7 +221,7 @@ public class TunnelRequestService {
} }
// Connect tunnel // Connect tunnel
tunnel = connection.connect(info); tunnel = connection.connect(info, tokens);
logger.info("User \"{}\" connected to connection \"{}\".", context.self().getIdentifier(), id); logger.info("User \"{}\" connected to connection \"{}\".", context.self().getIdentifier(), id);
break; break;
} }
@@ -235,7 +240,7 @@ public class TunnelRequestService {
} }
// Connect tunnel // Connect tunnel
tunnel = group.connect(info); tunnel = group.connect(info, tokens);
logger.info("User \"{}\" connected to group \"{}\".", context.self().getIdentifier(), id); logger.info("User \"{}\" connected to group \"{}\".", context.self().getIdentifier(), id);
break; break;
} }
@@ -385,16 +390,17 @@ public class TunnelRequestService {
GuacamoleClientInformation info = getClientInformation(request); GuacamoleClientInformation info = getClientInformation(request);
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
AuthenticatedUser authenticatedUser = session.getAuthenticatedUser();
UserContext userContext = session.getUserContext(authProviderIdentifier); UserContext userContext = session.getUserContext(authProviderIdentifier);
try { try {
// Create connected tunnel using provided connection ID and client information // Create connected tunnel using provided connection ID and client information
GuacamoleTunnel tunnel = createConnectedTunnel(userContext, type, id, info); GuacamoleTunnel tunnel = createConnectedTunnel(userContext, type,
id, info, new StandardTokenMap(authenticatedUser));
// Notify listeners to allow connection to be vetoed // Notify listeners to allow connection to be vetoed
fireTunnelConnectEvent(session.getAuthenticatedUser(), fireTunnelConnectEvent(authenticatedUser, authenticatedUser.getCredentials(), tunnel);
session.getAuthenticatedUser().getCredentials(), tunnel);
// Associate tunnel with session // Associate tunnel with session
return createAssociatedTunnel(tunnel, authToken, session, userContext, type, id); return createAssociatedTunnel(tunnel, authToken, session, userContext, type, id);