GUACAMOLE-38: Initial implementation of QuickConnect feature

This commit is contained in:
Nick Couchman
2017-06-06 11:09:01 -04:00
parent 2fb377ac81
commit facea42463
14 changed files with 1453 additions and 0 deletions

View File

@@ -0,0 +1,202 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-quickconnect</artifactId>
<packaging>jar</packaging>
<version>0.9.13-incubating</version>
<name>guacamole-auth-quickconnect</name>
<url>http://guacamole.incubator.apache.org/</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<!-- Written for 1.6 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArgs>
<arg>-Xlint:all</arg>
<arg>-Werror</arg>
</compilerArgs>
<fork>true</fork>
</configuration>
</plugin>
<!-- Copy dependencies prior to packaging -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>unpack-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!-- Assembly plugin - for easy distribution -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<finalName>${project.artifactId}-${project.version}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/assembly/dist.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-dist-archive</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- JS/CSS Minification Plugin -->
<plugin>
<groupId>com.samaxes.maven</groupId>
<artifactId>minify-maven-plugin</artifactId>
<version>1.7.5</version>
<executions>
<execution>
<id>default-cli</id>
<configuration>
<charset>UTF-8</charset>
<webappSourceDir>${basedir}/src/main/resources</webappSourceDir>
<webappTargetDir>${project.build.directory}/classes</webappTargetDir>
<cssSourceDir>/</cssSourceDir>
<cssTargetDir>/</cssTargetDir>
<cssFinalFile>quickconnect.css</cssFinalFile>
<cssSourceIncludes>
<cssSourceInclude>**/*.css</cssSourceInclude>
</cssSourceIncludes>
<jsSourceDir>/</jsSourceDir>
<jsTargetDir>/</jsTargetDir>
<jsFinalFile>quickconnect.js</jsFinalFile>
<jsSourceIncludes>
<jsSourceInclude>**/*.js</jsSourceInclude>
</jsSourceIncludes>
<!-- Do not minify and include tests -->
<jsSourceExcludes>
<jsSourceExclude>**/*.test.js</jsSourceExclude>
</jsSourceExcludes>
<jsEngine>CLOSURE</jsEngine>
<!-- Disable warnings for JSDoc annotations -->
<closureWarningLevels>
<misplacedTypeAnnotation>OFF</misplacedTypeAnnotation>
<nonStandardJsDocs>OFF</nonStandardJsDocs>
</closureWarningLevels>
</configuration>
<goals>
<goal>minify</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Verify format using Apache RAT -->
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
<version>0.12</version>
<configuration>
<excludes>
<exclude>**/*.json</exclude>
<exclude>src/licenses/**/*</exclude>
<exclude>src/main/resources/templates/*.html</exclude>
</excludes>
</configuration>
<!-- Bind RAT to validate phase -->
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Guacamole Extension API -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
<version>0.9.13-incubating</version>
<scope>provided</scope>
</dependency>
<!-- Guice -->
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-multibindings</artifactId>
<version>3.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>dist</id>
<baseDirectory>${project.artifactId}-${project.version}</baseDirectory>
<!-- Output tar.gz -->
<formats>
<format>tar.gz</format>
</formats>
<!-- Include licenses and extension .jar -->
<fileSets>
<!-- Include licenses -->
<fileSet>
<outputDirectory></outputDirectory>
<directory>src/licenses</directory>
</fileSet>
<!-- Include extension .jar -->
<fileSet>
<directory>target</directory>
<outputDirectory></outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,167 @@
/*
* 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.auth.quickconnect;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.HashMap;
import java.util.Map;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.simple.SimpleAuthenticationProvider;
import org.apache.guacamole.net.auth.UserContext;
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.LoggerFactory;
public class QuickConnectAuthenticationProvider extends SimpleAuthenticationProvider {
/**
* Logger for this class.
*/
private final Logger logger = LoggerFactory.getLogger(QuickConnectAuthenticationProvider.class);
private Map<String, GuacamoleConfiguration> quickConnections = new HashMap<String, GuacamoleConfiguration>();
private UserContext userContext = null;
@Inject
private Provider<QuickConnectUserContext> userContextProvider;
@Override
public String getIdentifier() {
return "quickconnect";
}
/**
* For QuickConnect, authenticateUser simply returns null because this
* extension is designed to provide only a connection directory to users
* that are already authenticated and not any actual authentication.
*
* @param credentials
* Credentials object passed in from Guacamole login.
*
* @returns
* Returns null, which causes the client to move on to the next
* module.
*/
@Override
public AuthenticatedUser authenticateUser(Credentials credentials)
throws GuacamoleException {
logger.debug(">>>QuickConnect<<< authenticateUser NOT IMPLEMENTED.");
GuacamoleConfiguration config = new GuacamoleConfiguration();
config.setProtocol("ssh");
config.setParameter("hostname","ussalxapps005t.cotyww.com");
config.setParameter("port","22");
quickConnections.put("Adhoc 1", config);
if (userContext == null)
userContext = new QuickConnectUserContext(this, credentials.getUsername(), quickConnections);
return null;
}
@Override
public Map<String, GuacamoleConfiguration>
getAuthorizedConfigurations(Credentials credentials)
throws GuacamoleException {
logger.debug(">>>QuickConnect<<< Retrieving configurations for user {}", credentials.getUsername());
GuacamoleConfiguration config = new GuacamoleConfiguration();
config.setProtocol("ssh");
config.setParameter("hostname","ussalxapps005t.cotyww.com");
config.setParameter("port","22");
quickConnections.put("Adhoc 1", config);
if(userContext == null)
userContext = new QuickConnectUserContext(this, credentials.getUsername(), quickConnections);
return quickConnections;
}
private Map<String, GuacamoleConfiguration>
getFilteredAuthorizedConfigurations(Credentials credentials)
throws GuacamoleException {
logger.debug(">>>QuickConnect<<< Filtering configurations.");
// 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;
}
private Map<String, GuacamoleConfiguration>
getFilteredAuthorizedConfigurations(AuthenticatedUser authenticatedUser)
throws GuacamoleException {
// Pull using credentials
return getFilteredAuthorizedConfigurations(authenticatedUser.getCredentials());
}
@Override
public UserContext getUserContext(AuthenticatedUser authenticatedUser)
throws GuacamoleException {
logger.debug(">>>QuickConnect<<< getUserContext for {}", authenticatedUser.getCredentials().getUsername());
// Get configurations
// Map<String, GuacamoleConfiguration> configs =
// getFilteredAuthorizedConfigurations(authenticatedUser);
// Return as unauthorized if not authorized to retrieve configs
// if (configs == null)
// return null;
// Return user context restricted to authorized configs
// return new QuickConnectUserContext(this, authenticatedUser.getIdentifier(), configs);
return userContext;
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.auth.quickconnect;
import com.google.inject.AbstractModule;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.net.auth.AuthenticationProvider;
/**
* Guice module to do QuickConnect injections.
*/
public class QuickConnectAuthenticationProviderModule extends AbstractModule {
/**
* Guacamole server environment.
*/
private final Environment environment;
/**
* QuickConnect authentication provider.
*/
private final AuthenticationProvider authProvider;
/**
* Create a new instance of the authentication provider module
* which configures injection for the QuickConnectAuthenticationProvider
* class.
*
* @param authProvider
* The authentication provider for which injection is being
* configured.
*
* @throws GuacamoleException
* If an error occurs while retrieving the server environment.
*/
public QuickConnectAuthenticationProviderModule(AuthenticationProvider authProvider)
throws GuacamoleException {
// Create a new local environment
this.environment = new LocalEnvironment();
// Initialize authProvider
this.authProvider = authProvider;
}
@Override
protected void configure() {
// Bind core implementations of guacamole-ext classes
bind(AuthenticationProvider.class).toInstance(authProvider);
bind(Environment.class).toInstance(environment);
// Bind QuickConnect-specific classes;
bind(QuickConnectDirectory.class);
bind(QuickConnectUserContext.class);
}
}

View File

@@ -0,0 +1,99 @@
/*
* 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.auth.quickconnect;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.AbstractConnectionGroup;
import org.apache.guacamole.net.auth.ConnectionGroup;
import org.apache.guacamole.protocol.GuacamoleClientInformation;
class QuickConnectConnectionGroup extends AbstractConnectionGroup {
/**
* The connection identifiers for this group.
*/
private Set<String> connectionIdentifiers;
public QuickConnectConnectionGroup(String name, String identifier) {
setName(name);
setIdentifier(identifier);
setType(ConnectionGroup.Type.ORGANIZATIONAL);
this.connectionIdentifiers = new HashSet<String>(Collections.<String>emptyList());
}
public QuickConnectConnectionGroup(String name, String identifier,
Collection<String> connectionIdentifiers) {
setName(name);
setIdentifier(identifier);
setType(ConnectionGroup.Type.ORGANIZATIONAL);
this.connectionIdentifiers = new HashSet<String>(connectionIdentifiers);
}
@Override
public int getActiveConnections() {
return 0;
}
@Override
public Set<String> getConnectionIdentifiers() {
return connectionIdentifiers;
}
@Override
public Set<String> getConnectionGroupIdentifiers() {
return Collections.<String>emptySet();
}
@Override
public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap();
}
@Override
public void setAttributes(Map<String, String> attributes) {
// Do nothing - there are no attributes
}
@Override
public GuacamoleTunnel connect(GuacamoleClientInformation info)
throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied.");
}
public String addConnectionIdentifier(String identifier) {
if (connectionIdentifiers.add(identifier))
return identifier;
return null;
}
}

View File

@@ -0,0 +1,103 @@
/*
* 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.auth.quickconnect;
import com.google.inject.Inject;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.ConnectionGroup;
import org.apache.guacamole.net.auth.simple.SimpleConnection;
import org.apache.guacamole.net.auth.simple.SimpleConnectionDirectory;
import org.apache.guacamole.net.auth.Connection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of the Connection Directory, stored
* completely in-memory.
*/
public class QuickConnectDirectory extends SimpleConnectionDirectory {
/**
* Logger for this class.
*/
private final Logger logger = LoggerFactory.getLogger(QuickConnectDirectory.class);
/**
* The unique identifier of the root connection group.
*/
private static final String ROOT_IDENTIFIER = "ROOT";
/**
* The root connection group for this directory.
*/
private final QuickConnectConnectionGroup rootGroup;
/**
* The internal counter for connection IDs.
*/
private int CONNECTION_ID = 0;
/**
* Creates a new QuickConnectDirectory which provides access to the
* connections contained within the given Map.
*
* @param connections
* A Collection of all connections that should be present in this
* connection directory.
*/
public QuickConnectDirectory(Collection<Connection> connections, ConnectionGroup rootGroup) {
super(connections);
this.rootGroup = (QuickConnectConnectionGroup)rootGroup;
logger.debug(">>>QuickConnect<<< Created new directory.");
}
private Integer getNextConnectionID() {
return CONNECTION_ID++;
}
@Override
public void add(Connection object) throws GuacamoleException {
logger.debug(">>>QuickConnect<<< Adding connection object.");
String connectionId = getNextConnectionID().toString();
object.setIdentifier(connectionId);
object.setParentIdentifier(ROOT_IDENTIFIER);
/*
SimpleConnection newConn = new SimpleConnection(
object.getName(),
connectionId,
object.getConfiguration()
);
newConn.setParentIdentifier(ROOT_IDENTIFIER);
*/
putConnection(new QuickConnection(object));
this.rootGroup.addConnectionIdentifier(connectionId);
}
@Override
public void update(Connection object) throws GuacamoleException {
logger.debug(">>>QuickConnect<<< Updating connection object.");
putConnection(object);
}
}

View File

@@ -0,0 +1,286 @@
/*
* 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.auth.quickconnect;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.form.Form;
import org.apache.guacamole.net.auth.ActiveConnection;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup;
import org.apache.guacamole.net.auth.ConnectionRecordSet;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.SharingProfile;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.net.auth.simple.SimpleConnection;
import org.apache.guacamole.net.auth.simple.SimpleConnectionGroup;
import org.apache.guacamole.net.auth.simple.SimpleConnectionGroupDirectory;
import org.apache.guacamole.net.auth.simple.SimpleConnectionRecordSet;
import org.apache.guacamole.net.auth.simple.SimpleDirectory;
import org.apache.guacamole.net.auth.simple.SimpleUser;
import org.apache.guacamole.net.auth.simple.SimpleUserDirectory;
import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An extremely simple UserContext implementation which provides access to
* a defined and restricted set of GuacamoleConfigurations. Access to
* querying or modifying either users or permissions is denied.
*/
public class QuickConnectUserContext implements UserContext {
/**
* Logger for this class.
*/
private final Logger logger = LoggerFactory.getLogger(QuickConnectUserContext.class);
/**
* The unique identifier of the root connection group.
*/
private static final String ROOT_IDENTIFIER = "ROOT";
/**
* The AuthenticationProvider that created this UserContext.
*/
private final AuthenticationProvider authProvider;
/**
* Reference to the user whose permissions dictate the configurations
* accessible within this UserContext.
*/
private final User self;
/**
* The Directory with access only to the User associated with this
* UserContext.
*/
private final Directory<User> userDirectory;
/**
* The Directory with access only to the root group associated with this
* UserContext.
*/
private final Directory<ConnectionGroup> connectionGroupDirectory;
/**
* The Directory with access to all connections within the root group
* associated with this UserContext.
*/
private final Directory<Connection> connectionDirectory;
/**
* The root connection group.
*/
private final ConnectionGroup rootGroup;
/**
* Creates a new SimpleUserContext which provides access to only those
* configurations within the given Map. The username is assigned
* arbitrarily.
*
* @param authProvider
* The AuthenticationProvider creating this UserContext.
*
* @param configs
* A Map of all configurations for which the user associated with this
* UserContext has read access.
*/
public QuickConnectUserContext(AuthenticationProvider authProvider,
Map<String, GuacamoleConfiguration> configs) {
this(authProvider, UUID.randomUUID().toString(), configs);
}
/**
* Creates a new SimpleUserContext for the user with the given username
* which provides access to only those configurations within the given Map.
*
* @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.
*/
public QuickConnectUserContext(AuthenticationProvider authProvider,
String username, Map<String, GuacamoleConfiguration> configs) {
Collection<String> connectionIdentifiers = new ArrayList<String>(configs.size());
Collection<String> connectionGroupIdentifiers = Collections.singleton(ROOT_IDENTIFIER);
// Produce collection of connections from given configs
Collection<Connection> connections = new ArrayList<Connection>(configs.size());
for (Map.Entry<String, GuacamoleConfiguration> configEntry : configs.entrySet()) {
// Get connection identifier and configuration
String identifier = configEntry.getKey();
GuacamoleConfiguration config = configEntry.getValue();
// Add as simple connection
Connection connection = new SimpleConnection(identifier, identifier, config);
connection.setParentIdentifier(ROOT_IDENTIFIER);
connections.add(connection);
// Add identifier to overall set of identifiers
connectionIdentifiers.add(identifier);
}
// Add root group that contains only the given configurations
this.rootGroup = new QuickConnectConnectionGroup(
ROOT_IDENTIFIER, ROOT_IDENTIFIER,
connectionIdentifiers
);
// Build new user from credentials
this.self = new SimpleUser(username, connectionIdentifiers,
connectionGroupIdentifiers);
// Create directories for new user
this.userDirectory = new SimpleUserDirectory(self);
this.connectionDirectory = new QuickConnectDirectory(connections,this.rootGroup);
this.connectionGroupDirectory = new SimpleConnectionGroupDirectory(Collections.singleton(this.rootGroup));
// Associate provided AuthenticationProvider
this.authProvider = authProvider;
}
public QuickConnectUserContext(AuthenticationProvider authProvider,
String username) {
this.rootGroup = new QuickConnectConnectionGroup(
ROOT_IDENTIFIER, ROOT_IDENTIFIER
);
this.self = new SimpleUser(username,
Collections.<String>emptyList(),
Collections.singleton(ROOT_IDENTIFIER)
);
this.userDirectory = new SimpleUserDirectory(self);
this.connectionDirectory = new QuickConnectDirectory(Collections.<Connection>emptyList(), this.rootGroup);
this.connectionGroupDirectory = new SimpleConnectionGroupDirectory(Collections.singleton(this.rootGroup));
this.authProvider = authProvider;
}
public void setConfigs(Map<String, GuacamoleConfiguration> configs) {
return;
}
@Override
public User self() {
return self;
}
@Override
public Object getResource() throws GuacamoleException {
return null;
}
@Override
public AuthenticationProvider getAuthenticationProvider() {
return authProvider;
}
@Override
public Directory<User> getUserDirectory()
throws GuacamoleException {
return userDirectory;
}
@Override
public Directory<Connection> getConnectionDirectory()
throws GuacamoleException {
logger.debug(">>>QuickConnect<<< Returning the entire connection directory: {}", connectionDirectory.getIdentifiers());
return connectionDirectory;
}
@Override
public Directory<ConnectionGroup> getConnectionGroupDirectory()
throws GuacamoleException {
logger.debug(">>>QuickConnect<<< Returning the entire group directory: {}", connectionGroupDirectory.getIdentifiers());
return connectionGroupDirectory;
}
@Override
public ConnectionGroup getRootConnectionGroup() throws GuacamoleException {
logger.debug(">>>QuickConnect<<< Returning the entire root Connection Group: {}", rootGroup);
return rootGroup;
}
@Override
public Directory<SharingProfile> getSharingProfileDirectory()
throws GuacamoleException {
return new SimpleDirectory<SharingProfile>();
}
@Override
public Directory<ActiveConnection> getActiveConnectionDirectory()
throws GuacamoleException {
return new SimpleDirectory<ActiveConnection>();
}
@Override
public ConnectionRecordSet getConnectionHistory()
throws GuacamoleException {
return new SimpleConnectionRecordSet();
}
@Override
public Collection<Form> getUserAttributes() {
return Collections.<Form>emptyList();
}
@Override
public Collection<Form> getConnectionAttributes() {
return Collections.<Form>emptyList();
}
@Override
public Collection<Form> getConnectionGroupAttributes() {
return Collections.<Form>emptyList();
}
@Override
public Collection<Form> getSharingProfileAttributes() {
return Collections.<Form>emptyList();
}
}

View File

@@ -0,0 +1,146 @@
/*
* 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.auth.quickconnect;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.net.GuacamoleSocket;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.InetGuacamoleSocket;
import org.apache.guacamole.net.SSLGuacamoleSocket;
import org.apache.guacamole.net.SimpleGuacamoleTunnel;
import org.apache.guacamole.net.auth.AbstractConnection;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionRecord;
import org.apache.guacamole.net.auth.GuacamoleProxyConfiguration;
import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket;
import org.apache.guacamole.protocol.GuacamoleClientInformation;
import org.apache.guacamole.protocol.GuacamoleConfiguration;
public class QuickConnection extends AbstractConnection {
/**
* Backing configuration, containing all sensitive information.
*/
private GuacamoleConfiguration config;
/**
* Number of active connections.
*/
private int activeConnections;
public QuickConnection() {
}
public QuickConnection(String name, String identifier,
GuacamoleConfiguration config) {
setName(name);
setIdentifier(identifier);
setConfiguration(config);
this.config = config;
this.activeConnections = 0;
}
public QuickConnection(Connection object) {
setName(object.getName());
setIdentifier(object.getIdentifier());
setParentIdentifier(object.getParentIdentifier());
setConfiguration(object.getConfiguration());
this.config = object.getConfiguration();
this.activeConnections = 0;
}
@Override
public int getActiveConnections() {
return activeConnections;
}
@Override
public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap();
}
@Override
public void setAttributes(Map<String, String> attributes) {
// Do nothing - there are no attributes
}
@Override
public GuacamoleTunnel connect(GuacamoleClientInformation info)
throws GuacamoleException {
// Retrieve proxy configuration from environment
Environment environment = new LocalEnvironment();
GuacamoleProxyConfiguration proxyConfig = environment.getDefaultGuacamoleProxyConfiguration();
// Get guacd connection parameters
String hostname = proxyConfig.getHostname();
int port = proxyConfig.getPort();
GuacamoleSocket socket;
// Determine socket type based on required encryption method
switch (proxyConfig.getEncryptionMethod()) {
// If guacd requires SSL, use it
case SSL:
socket = new ConfiguredGuacamoleSocket(
new SSLGuacamoleSocket(hostname, port),
config, info
);
break;
// Connect directly via TCP if encryption is not enabled
case NONE:
socket = new ConfiguredGuacamoleSocket(
new InetGuacamoleSocket(hostname, port),
config, info
);
break;
// Abort if encryption method is unknown
default:
throw new GuacamoleServerException("Unimplemented encryption method.");
}
return new SimpleGuacamoleTunnel(socket);
}
@Override
public List<ConnectionRecord> getHistory() throws GuacamoleException {
return Collections.<ConnectionRecord>emptyList();
}
}

View File

@@ -0,0 +1,207 @@
/*
* 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.
*/
/**
* The controller for making ad-hoc (quick) connections
*/
angular.module('guacQuickConnect').controller('quickconnectController', ['$scope', '$injector', '$log',
function manageConnectionController($scope, $injector, $log) {
$log.debug('In quickconnectController...');
// Required types
var ClientIdentifier = $injector.get('ClientIdentifier');
var Connection = $injector.get('Connection');
// Required services
var $location = $injector.get('$location');
var $routeParams = $injector.get('$routeParams');
var guacNotification = $injector.get('guacNotification');
var connectionService = $injector.get('connectionService');
var schemaService = $injector.get('schemaService');
/**
* An action to be provided along with the object sent to showStatus which
* closes the currently-shown status dialog.
*/
var ACKNOWLEDGE_ACTION = {
name : "MANAGE_CONNECTION.ACTION_ACKNOWLEDGE",
// Handle action
callback : function acknowledgeCallback() {
guacNotification.showStatus(false);
}
};
$scope.uri = null;
$scope.selectedDataSource = 'quickconnect';
/**
* Saves the connection, creating a new connection or updating the existing
* connection.
*/
$scope.quickConnect = function quickConnect() {
$log.debug('Got saveConnection() call.');
// Construct parameters from URI...
/**
* Parse URL into the following components:
* [0] - Full URL
* [3] - Protocol
* [5] - Username
* [7] - Password
* [8] - Hostname
* [10] - Port
* [11] - Path
* [13] - Document
* [15] - Parameters
* [17] - JS Route
*/
var regexURL = /^(((rdp|ssh|telnet|vnc)?):\/)?\/?((.*?)(:(.*?)|)@)?([^:\/\s]+)(:([^\/]*))?((\/\w+\/)*)([-\w.\/]+[^#?\s]*)?(\?([^#]*))?(#(.*))?$/g;
$log.debug(regexURL);
var urlArray = regexURL.exec($scope.uri);
$log.debug($scope.uri);
$log.debug(urlArray);
var gettingProtocols = schemaService.getProtocols('quickconnect');
gettingProtocols.success(function checkProtocol(supportedProtocols) {
$log.debug(supportedProtocols);
if (!(urlArray[3] in supportedProtocols)) {
guacNotification.showStatus({
'className' : 'error',
'title' : 'Unsupported Protocol',
'text' : 'The ' + urlArray[3] + ' protocol is not supported by Guacamole.',
'actions' : [ ACKNOWLEDGE_ACTION ]
});
return;
}
var port = 0;
var urlParams = Array();
switch(urlArray[3]) {
case 'rdp':
port = 3389;
break;
case 'ssh':
port = 22;
break;
case 'telnet':
port = 23;
break;
case 'vnc':
port = 5900;
break;
default:
port = 0;
}
if (!isNaN(urlArray[10]))
port = parseInt(urlArray[10]);
if (!(typeof urlArray[15] === 'undefined'))
urlParams = JSON.parse('{"' + decodeURI(urlArray[15]).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}');
console.log(urlParams);
var connParams = {};
if (!(typeof urlArray[8] === 'undefined'))
connParams['hostname'] = urlArray[8];
else
connParams['hostname'] = 'localhost';
if (!(typeof urlArray[5] === 'undefined'))
connParams['username'] = urlArray[5];
if (!(typeof urlArray[7] === 'undefined'))
connParams['password'] = urlArray[7];
connParams['port'] = port;
connParams['read-only'] = 'read-only' in urlParams ? urlParams['read-only'] : '';
connParams['swap-red-blue'] = 'swap-red-blue' in urlParams ? urlParams['swap-red-blue'] : '';
connParams['cursor'] = 'cursor' in urlParams ? urlParams['cursor'] : '';
connParams['color-depth'] = 'color-depth' in urlParams ? parseInt(urlParams['color-depth']) : '';
connParams['clipboard-encoding'] = 'clipboard-encoding' in urlParams ? urlParams['clipboard-encoding'] : '';
connParams['dest-port'] = 'dest-port' in urlParams ? parseInt(urlParams['dest-port']) : '';
connParams['create-recording-path'] = 'create-recording-path' in urlParams ? urlParams['create-recording-path'] : '';
connParams['enable-sftp'] = 'enable-sftp' in urlParams ? urlParams['enable-sftp'] : false;
connParams['sftp-port'] = 'sftp-port' in urlParams ? parseInt(urlParams['sftp-port']) : 22;
connParams['enable-audio'] = 'enable-audio' in urlParams ? urlParams['enable-audio'] : false;
connParams['color-scheme'] = 'color-scheme' in urlParams ? urlParams['color-scheme'] : '';
connParams['font-size'] = 'font-size' in urlParams ? parseInt(urlParams['font-size']) : '';
connParams['create-typescript-path'] = 'create-typescript-path' in urlParams ? urlParams['create-typescript-path'] : '';
connParams['private-key'] = 'private-key' in urlParams ? urlParams['private-key'] : '';
connParams['passphrase'] = 'passphrase' in urlParams ? urlParams['passphrase'] : '';
var connName = urlArray[3] + '://';
if(!(typeof connParams['username'] === 'undefined'))
connName += connParams['username'] + '@';
connName += connParams['hostname'] + ':' + connParams['port'];
$scope.connection = new Connection({
name : connName,
protocol : urlArray[3],
parameters: connParams
});
console.log($scope.connection);
connectionService.saveConnection($scope.selectedDataSource, $scope.connection)
.success(function runConnection(newConnection) {
$log.debug('Connection saved - we should run it, now: ' + newConnection.identifier);
$location.url('/client/' + ClientIdentifier.toString({
dataSource : $scope.selectedDataSource,
type : ClientIdentifier.Types.CONNECTION,
id : newConnection.identifier
}));
})
.error(function saveFailed(error) {
guacNotification.showStatus({
'className' : 'error',
'title' : 'MANAGE_CONNECTION.DIALOG_HEADER_ERROR',
'text' : error.translatableMessage,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
});
});
return;
/*
$scope.connection.parameters = $scope.parameters;
// Save the connection
connectionService.saveConnection($scope.selectedDataSource, $scope.connection)
.success(function savedConnection(newConnection) {
$log.debug('Connection saved successfully: ' + newConnection);
// $location.url('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections');
})
// Notify of any errors
.error(function connectionSaveFailed(error) {
guacNotification.showStatus({
'className' : 'error',
'title' : 'MANAGE_CONNECTION.DIALOG_HEADER_ERROR',
'text' : error.translatableMessage,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
});
*/
};
}]);

View File

@@ -0,0 +1,27 @@
{
"guacamoleVersion" : "0.9.13-incubating",
"name" : "Adhoc Guacamole Connections",
"namespace" : "quickconnect",
"authProviders" : [
"org.apache.guacamole.auth.quickconnect.QuickConnectAuthenticationProvider"
],
"js" : [
"quickconnect.min.js"
],
"css" : [
"quickconnect.min.css"
],
"html" : [
"templates/quickconnectField.html"
],
"resources" : {
"templates/quickconnectField.html" : "text/html"
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.
*/
/**
* Module which provides Adhoc connection capability
*/
angular.module('guacQuickConnect', [
'form'
]);
// Ensure the guacQuickConnect module is loaded along with the rest of the app
angular.module('index').requires.push('guacQuickConnect');

View File

@@ -0,0 +1,48 @@
/*
* 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.
*/
.quickconnect-container {
margin: 0.5em 0;
width: 100%;
}
.quickconnect-container .quickconnect-field {
background-image: url('images/protocol-icons/guac-text.png');
background-repeat: no-repeat;
background-size: 1.75em;
background-position: 0.25em center;
background-color: transparent;
padding: 0.5em;
padding-left: 2.25em;
width: 100%;
max-width: none;
border: 0;
border-left: 1px solid rgba(0,0,0,0.125);
box-sizing: border-box;
}
.quickconnect-field input[type="submit"] {
display: none !important;
}
.quickconnect-button {
clear: both;
float: right;
}

View File

@@ -0,0 +1,7 @@
<meta name="before" content=".recent-connections" />
<div class="header" ng-controller="quickconnectController">
<div class="quickconnect-container">
<input type=text class="quickconnect-field" placeholder="Enter Connection URI" ng-model="uri" />
</div>
<button class="quickconnect-button" ng-click="quickConnect()">Connect</button>
</div>