diff --git a/extensions/guacamole-auth-quickconnect/pom.xml b/extensions/guacamole-auth-quickconnect/pom.xml new file mode 100644 index 000000000..9d2bcfb17 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/pom.xml @@ -0,0 +1,202 @@ + + + + + 4.0.0 + org.apache.guacamole + guacamole-auth-quickconnect + jar + 0.9.13-incubating + guacamole-auth-quickconnect + http://guacamole.incubator.apache.org/ + + + UTF-8 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + 1.6 + 1.6 + + -Xlint:all + -Werror + + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + unpack-dependencies + prepare-package + + unpack-dependencies + + + runtime + ${project.build.directory}/classes + + + + + + + + maven-assembly-plugin + 2.5.3 + + ${project.artifactId}-${project.version} + false + + src/main/assembly/dist.xml + + + + + make-dist-archive + package + + single + + + + + + + + com.samaxes.maven + minify-maven-plugin + 1.7.5 + + + default-cli + + UTF-8 + + ${basedir}/src/main/resources + ${project.build.directory}/classes + + / + / + quickconnect.css + + + **/*.css + + + / + / + quickconnect.js + + + **/*.js + + + + + **/*.test.js + + CLOSURE + + + + OFF + OFF + + + + + minify + + + + + + + + org.apache.rat + apache-rat-plugin + 0.12 + + + + **/*.json + src/licenses/**/* + src/main/resources/templates/*.html + + + + + + + validate + validate + + check + + + + + + + + + + + + + + org.apache.guacamole + guacamole-ext + 0.9.13-incubating + provided + + + + + com.google.inject + guice + 3.0 + + + com.google.inject.extensions + guice-multibindings + 3.0 + + + + + diff --git a/extensions/guacamole-auth-quickconnect/src/main/assembly/dist.xml b/extensions/guacamole-auth-quickconnect/src/main/assembly/dist.xml new file mode 100644 index 000000000..b89fd534c --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/assembly/dist.xml @@ -0,0 +1,53 @@ + + + + + dist + ${project.artifactId}-${project.version} + + + + tar.gz + + + + + + + + + src/licenses + + + + + target + + + *.jar + + + + + + diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectAuthenticationProvider.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectAuthenticationProvider.java new file mode 100644 index 000000000..a3add1185 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectAuthenticationProvider.java @@ -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 quickConnections = new HashMap(); + + private UserContext userContext = null; + + @Inject + private Provider 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 + 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 + getFilteredAuthorizedConfigurations(Credentials credentials) + throws GuacamoleException { + + logger.debug(">>>QuickConnect<<< Filtering configurations."); + + // Get configurations + Map 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 + 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 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; + + } + +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectAuthenticationProviderModule.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectAuthenticationProviderModule.java new file mode 100644 index 000000000..54f7c4163 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectAuthenticationProviderModule.java @@ -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); + + } + +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectConnectionGroup.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectConnectionGroup.java new file mode 100644 index 000000000..94d00d7c6 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectConnectionGroup.java @@ -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 connectionIdentifiers; + + public QuickConnectConnectionGroup(String name, String identifier) { + + setName(name); + setIdentifier(identifier); + setType(ConnectionGroup.Type.ORGANIZATIONAL); + + this.connectionIdentifiers = new HashSet(Collections.emptyList()); + + } + + public QuickConnectConnectionGroup(String name, String identifier, + Collection connectionIdentifiers) { + + setName(name); + setIdentifier(identifier); + setType(ConnectionGroup.Type.ORGANIZATIONAL); + + this.connectionIdentifiers = new HashSet(connectionIdentifiers); + + } + + @Override + public int getActiveConnections() { + return 0; + } + + @Override + public Set getConnectionIdentifiers() { + return connectionIdentifiers; + } + + @Override + public Set getConnectionGroupIdentifiers() { + return Collections.emptySet(); + } + + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + @Override + public void setAttributes(Map 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; + } + +} 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 new file mode 100644 index 000000000..f77a5a78c --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectDirectory.java @@ -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 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); + } + +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java new file mode 100644 index 000000000..02038604d --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java @@ -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 userDirectory; + + /** + * The Directory with access only to the root group associated with this + * UserContext. + */ + private final Directory connectionGroupDirectory; + + /** + * The Directory with access to all connections within the root group + * associated with this UserContext. + */ + private final Directory 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 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 configs) { + + Collection connectionIdentifiers = new ArrayList(configs.size()); + Collection connectionGroupIdentifiers = Collections.singleton(ROOT_IDENTIFIER); + + // Produce collection of connections from given configs + Collection connections = new ArrayList(configs.size()); + for (Map.Entry 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.emptyList(), + Collections.singleton(ROOT_IDENTIFIER) + ); + + this.userDirectory = new SimpleUserDirectory(self); + this.connectionDirectory = new QuickConnectDirectory(Collections.emptyList(), this.rootGroup); + this.connectionGroupDirectory = new SimpleConnectionGroupDirectory(Collections.singleton(this.rootGroup)); + + this.authProvider = authProvider; + + } + + public void setConfigs(Map 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 getUserDirectory() + throws GuacamoleException { + return userDirectory; + } + + @Override + public Directory getConnectionDirectory() + throws GuacamoleException { + + logger.debug(">>>QuickConnect<<< Returning the entire connection directory: {}", connectionDirectory.getIdentifiers()); + + return connectionDirectory; + } + + @Override + public Directory 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 getSharingProfileDirectory() + throws GuacamoleException { + return new SimpleDirectory(); + } + + @Override + public Directory getActiveConnectionDirectory() + throws GuacamoleException { + return new SimpleDirectory(); + } + + @Override + public ConnectionRecordSet getConnectionHistory() + throws GuacamoleException { + return new SimpleConnectionRecordSet(); + } + + @Override + public Collection
getUserAttributes() { + return Collections.emptyList(); + } + + @Override + public Collection getConnectionAttributes() { + return Collections.emptyList(); + } + + @Override + public Collection getConnectionGroupAttributes() { + return Collections.emptyList(); + } + + @Override + public Collection getSharingProfileAttributes() { + return Collections.emptyList(); + } + +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnection.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnection.java new file mode 100644 index 000000000..f675057b6 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnection.java @@ -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 getAttributes() { + return Collections.emptyMap(); + } + + @Override + public void setAttributes(Map 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 getHistory() throws GuacamoleException { + return Collections.emptyList(); + } + +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/resources/controllers/quickconnectController.js b/extensions/guacamole-auth-quickconnect/src/main/resources/controllers/quickconnectController.js new file mode 100644 index 000000000..1a120a89a --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/resources/controllers/quickconnectController.js @@ -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 ] + }); + }); + */ + + }; + +}]); diff --git a/extensions/guacamole-auth-quickconnect/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-quickconnect/src/main/resources/guac-manifest.json new file mode 100644 index 000000000..0aa96b1db --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/resources/guac-manifest.json @@ -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" + } + +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/resources/quickconnectModule.js b/extensions/guacamole-auth-quickconnect/src/main/resources/quickconnectModule.js new file mode 100644 index 000000000..7635a1ed2 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/resources/quickconnectModule.js @@ -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'); diff --git a/extensions/guacamole-auth-quickconnect/src/main/resources/styles/quickconnect.css b/extensions/guacamole-auth-quickconnect/src/main/resources/styles/quickconnect.css new file mode 100644 index 000000000..49a407b2a --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/resources/styles/quickconnect.css @@ -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; +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/resources/templates/quickconnectField.html b/extensions/guacamole-auth-quickconnect/src/main/resources/templates/quickconnectField.html new file mode 100644 index 000000000..0f53379f8 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/resources/templates/quickconnectField.html @@ -0,0 +1,7 @@ + +
+
+ +
+ +
diff --git a/pom.xml b/pom.xml index 4f7c8ed6a..e5e631fa9 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,7 @@ extensions/guacamole-auth-jdbc extensions/guacamole-auth-ldap extensions/guacamole-auth-openid + extensions/guacamole-auth-quickconnect extensions/guacamole-auth-totp