diff --git a/extensions/guacamole-auth-quickconnect/pom.xml b/extensions/guacamole-auth-quickconnect/pom.xml new file mode 100644 index 000000000..b2fd92286 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/pom.xml @@ -0,0 +1,206 @@ + + + + + 4.0.0 + org.apache.guacamole + guacamole-auth-quickconnect + jar + 0.9.14 + guacamole-auth-quickconnect + http://guacamole.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.14 + provided + + + + + javax.ws.rs + jsr311-api + 1.1.1 + provided + + + + + junit + junit + 4.10 + test + + + + + diff --git a/extensions/guacamole-auth-quickconnect/src/licenses/LICENSE b/extensions/guacamole-auth-quickconnect/src/licenses/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/licenses/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/extensions/guacamole-auth-quickconnect/src/licenses/NOTICE b/extensions/guacamole-auth-quickconnect/src/licenses/NOTICE new file mode 100644 index 000000000..97e613062 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/licenses/NOTICE @@ -0,0 +1,5 @@ +Apache Guacamole +Copyright 2018 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). 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..090c5e7f7 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectAuthenticationProvider.java @@ -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. + */ + +package org.apache.guacamole.auth.quickconnect; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.AuthenticatedUser; +import org.apache.guacamole.net.auth.AbstractAuthenticationProvider; +import org.apache.guacamole.net.auth.UserContext; + +/** + * This class provides the necessary hooks into the Guacamole Client authentication + * process so that the QuickConnect functionality can be initialized and used + * throughout the web client. + */ +public class QuickConnectAuthenticationProvider extends AbstractAuthenticationProvider { + + @Override + public String getIdentifier() { + return "quickconnect"; + } + + @Override + public UserContext getUserContext(AuthenticatedUser authenticatedUser) + throws GuacamoleException { + + return new QuickConnectUserContext(this, + authenticatedUser.getIdentifier()); + + } + +} 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..37b07ba33 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectDirectory.java @@ -0,0 +1,122 @@ +/* + * 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.concurrent.ConcurrentHashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.quickconnect.utility.QCParser; +import org.apache.guacamole.net.auth.ConnectionGroup; +import org.apache.guacamole.net.auth.simple.SimpleConnection; +import org.apache.guacamole.net.auth.simple.SimpleDirectory; +import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.protocol.GuacamoleConfiguration; + +/** + * Implementation of a directory to store Connection objects + * completely in memory. + */ +public class QuickConnectDirectory extends SimpleDirectory { + + /** + * The connections to store. + */ + private final Map connections = + new ConcurrentHashMap(); + + /** + * The root connection group for this directory. + */ + private final QuickConnectionGroup rootGroup; + + /** + * The internal counter for connection IDs. + */ + private final AtomicInteger connectionId; + + /** + * Creates a new QuickConnectDirectory with the default + * empty Map for Connection objects, and the specified + * ConnectionGroup at the root of the directory. + * + * @param rootGroup + * A group that should be at the root of this directory. + */ + public QuickConnectDirectory(ConnectionGroup rootGroup) { + this.rootGroup = (QuickConnectionGroup)rootGroup; + this.connectionId = new AtomicInteger(); + super.setObjects(this.connections); + } + + /** + * Returns the current connection identifier counter and + * then increments it. + * + * @return + * An int representing the next available connection + * identifier to be used when adding connections. + */ + private int getNextConnectionID() { + return connectionId.getAndIncrement(); + } + + @Override + public void add(Connection connection) throws GuacamoleException { + connections.put(connection.getIdentifier(), connection); + } + + /** + * Create a SimpleConnection object from a GuacamoleConfiguration, + * obtain an identifier, and place it on the tree, returning the + * identifier value of the new connection. + * + * @param config + * The GuacamoleConfiguration object to use to create the + * SimpleConnection object. + * + * @return + * The identifier of the connection created in the directory. + * + * @throws GuacamoleException + * If an error occurs adding the object to the tree. + */ + public String create(GuacamoleConfiguration config) throws GuacamoleException { + + // Get the next available connection identifier. + String newConnectionId = Integer.toString(getNextConnectionID()); + + // Generate a name for the configuration. + String name = QCParser.getName(config); + + // Create a new connection and set the parent identifier. + Connection connection = new SimpleConnection(name, newConnectionId, config); + connection.setParentIdentifier(QuickConnectUserContext.ROOT_IDENTIFIER); + + // Place the object in this directory. + add(connection); + + // Add connection to the tree. + rootGroup.addConnectionIdentifier(newConnectionId); + + return newConnectionId; + } + +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectException.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectException.java new file mode 100644 index 000000000..6e4d5b94c --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectException.java @@ -0,0 +1,86 @@ +/* + * 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 org.apache.guacamole.GuacamoleClientException; +import org.apache.guacamole.language.Translatable; +import org.apache.guacamole.language.TranslatableMessage; + +/** + * An exception that is thrown by this extension when an error occurs + * attempting to create and establish a connection with a user-provided + * URI. + */ +public class QuickConnectException extends GuacamoleClientException + implements Translatable { + + /** + * A message that can be passed through the translation service + * to provide information about the error that occurred. + */ + private final TranslatableMessage translatableMessage; + + /** + * Create a QuickConnectException with the given message and translationKey. + * The message will not be passed through the translation system; the + * translationKey will be passed through the translation system. Both should + * describe the error. + * + * @param message + * A string describing the error that occurred when trying to create + * or establish the connection. This will not be passed through the + * translation system. + * + * @param translationKey + * A key known to the translation system describing the error that + * occurred when trying to create or establish the connection. + * This will be passed through the translation system to provide + * a localized version of the message. + */ + public QuickConnectException(String message, String translationKey) { + super(message); + this.translatableMessage = new TranslatableMessage(translationKey); + } + + /** + * Create a new QuickConnectException given the human-readable message, + * which will not be passed through the translation system, and the + * translatableMessage, which will be passed through the translation system. + * Both parameters should describe the error preventing the connection + * from being created or established. + * + * @param message + * The human-readable message describing the error, which will not + * be passed through the translation system. + * + * @param translatableMessage + * The human-readable message describing the error, which will be + * passed through the translation system. + */ + public QuickConnectException(String message, TranslatableMessage translatableMessage) { + super(message); + this.translatableMessage = translatableMessage; + } + + @Override + public TranslatableMessage getTranslatableMessage() { + return translatableMessage; + } + +} 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..d7e23edb4 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java @@ -0,0 +1,131 @@ +/* + * 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 org.apache.guacamole.auth.quickconnect.rest.QuickConnectREST; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.AbstractUserContext; +import org.apache.guacamole.net.auth.AuthenticationProvider; +import org.apache.guacamole.net.auth.ConnectionGroup; +import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.simple.SimpleUser; + +/** + * A simple implementation of UserContext to support this + * extension, used for storing connections the user has created + * with the QuickConnect bar in the webapp. + */ +public class QuickConnectUserContext extends AbstractUserContext { + + /** + * The unique identifier of the root connection group. + */ + public static final String ROOT_IDENTIFIER = DEFAULT_ROOT_CONNECTION_GROUP; + + /** + * 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 to all connections within the root group + * associated with this UserContext. + */ + private final QuickConnectDirectory connectionDirectory; + + /** + * The root connection group. + */ + private final ConnectionGroup rootGroup; + + /** + * Construct a QuickConnectUserContext using the authProvider and + * the username. + * + * @param authProvider + * The authentication provider module instantiating this + * this class. + * + * @param username + * The name of the user logging in that will be associated + * with this UserContext. + * + * @throws GuacamoleException + * If errors occur initializing the ConnectionGroup, + * ConnectionDirectory, or User. + */ + public QuickConnectUserContext(AuthenticationProvider authProvider, + String username) throws GuacamoleException { + + // Initialize the rootGroup to a QuickConnectionGroup with a + // single root identifier. + this.rootGroup = new QuickConnectionGroup( + ROOT_IDENTIFIER, + ROOT_IDENTIFIER + ); + + // Initialize the connection directory + this.connectionDirectory = new QuickConnectDirectory(this.rootGroup); + + // Initialize the user to a SimpleUser with the provided username, + // no connections, and the single root group. + this.self = new SimpleUser(username, + connectionDirectory.getIdentifiers(), + Collections.singleton(ROOT_IDENTIFIER) + ); + + // Set the authProvider to the calling authProvider object. + this.authProvider = authProvider; + + } + + @Override + public QuickConnectDirectory getConnectionDirectory() { + return connectionDirectory; + } + + @Override + public User self() { + return self; + } + + @Override + public Object getResource() throws GuacamoleException { + return new QuickConnectREST(connectionDirectory); + } + + @Override + public AuthenticationProvider getAuthenticationProvider() { + return authProvider; + } + + @Override + public ConnectionGroup getRootConnectionGroup() throws GuacamoleException { + return rootGroup; + } + +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectionGroup.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectionGroup.java new file mode 100644 index 000000000..cbce37976 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectionGroup.java @@ -0,0 +1,117 @@ +/* + * 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.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; + +/** + * Provides a simple, single-level connection group used for + * temporarily storing the Connection objects created by users. + */ +public class QuickConnectionGroup extends AbstractConnectionGroup { + + /** + * A set that will store the Connection identifiers for this group. + */ + private final Set connectionIdentifiers = + new HashSet(Collections.emptyList()); + + /** + * Set up a QuickConnectionGroup with the provided name and + * identifier. + * + * @param name + * The name of the QuickConnectionGroup. + * + * @param identifier + * The identifier of the QuickConnectionGroup. + */ + public QuickConnectionGroup(String name, String identifier) { + + super(); + super.setName(name); + super.setIdentifier(identifier); + super.setType(ConnectionGroup.Type.ORGANIZATIONAL); + + } + + /** + * Add a connection identifier to this connection group, and + * return the identifier if the add succeeds, otherwise + * return null. + * + * @param identifier + * The identifier of the connection to add to the group. + * + * @return + * The String identifier of the connection if the add + * operation was successful; otherwise null. + */ + public String addConnectionIdentifier(String identifier) { + if (connectionIdentifiers.add(identifier)) + return identifier; + return null; + } + + @Override + public int getActiveConnections() { + // This group does not track active connections. + return 0; + } + + @Override + public Set getConnectionIdentifiers() { + return connectionIdentifiers; + } + + @Override + public Set getConnectionGroupIdentifiers() { + // This group contains only connections, not other groups. + return Collections.emptySet(); + } + + @Override + public Map getAttributes() { + // There are no attributes associated with this group. + return Collections.emptyMap(); + } + + @Override + public void setAttributes(Map attributes) { + // Do nothing - there are no attributes + } + + @Override + public GuacamoleTunnel connect(GuacamoleClientInformation info) + throws GuacamoleException { + // This group does not support connections + throw new GuacamoleSecurityException("Permission denied."); + } + +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/rest/QuickConnectREST.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/rest/QuickConnectREST.java new file mode 100644 index 000000000..4cced071c --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/rest/QuickConnectREST.java @@ -0,0 +1,82 @@ +/* + * 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.rest; + +import java.util.Collections; +import java.util.Map; +import javax.ws.rs.FormParam; +import javax.ws.rs.Path; +import javax.ws.rs.POST; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.quickconnect.QuickConnectDirectory; +import org.apache.guacamole.auth.quickconnect.utility.QCParser; + +/** + * A class that implements REST endpoints for the QuickConnect + * extension. + */ +@Produces(MediaType.APPLICATION_JSON) +public class QuickConnectREST { + + /** + * The connection directory for this REST endpoint. + */ + private final QuickConnectDirectory directory; + + /** + * Construct a new QuickConnectREST class, taking in a + * QuickConnectDirectory for use with this class. + * + * @param directory + * The QuickConnectDirectory object to associate with this + * REST endpoint class. + */ + public QuickConnectREST(QuickConnectDirectory directory) { + this.directory = directory; + } + + /** + * Parse the URI read from the POST input, add the connection + * to the directory, and return a Map containing a single key, + * identifier, and the identifier of the new connection. + * + * @param uri + * The URI to parse into a connection. + * + * @return + * A Map containing a single key, identifier, and the + * identifier of the new connection. + * + * @throws GuacamoleException + * If an error is encountered parsing the URI. + */ + @POST + @Path("create") + public Map create(@FormParam("uri") String uri) + throws GuacamoleException { + + return Collections.singletonMap("identifier", + directory.create(QCParser.getConfiguration(uri))); + + } + +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/utility/QCParser.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/utility/QCParser.java new file mode 100644 index 000000000..3e2e5e5ab --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/utility/QCParser.java @@ -0,0 +1,256 @@ +/* + * 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.utility; + +import java.io.UnsupportedEncodingException; +import java.lang.StringBuilder; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.guacamole.GuacamoleServerException; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.quickconnect.QuickConnectException; +import org.apache.guacamole.protocol.GuacamoleConfiguration; + +/** + * A utility class to parse out a URI into the settings necessary + * to generate a GuacamoleConfiguration object. + */ +public class QCParser { + + /** + * The regex to use to split username and password. + */ + private static final Pattern userinfoPattern = Pattern.compile("(^[^:]+):?(.*)"); + + /** + * The regex group of the username. + */ + private static final int USERNAME_GROUP = 1; + + /** + * THe regex group of the password. + */ + private static final int PASSWORD_GROUP = 2; + + /** + * Parse out a URI string and get a GuacamoleConfiguration + * from that string, or an exception if the parsing fails. + * + * @param uri + * The string form of the URI to be parsed. + * + * @return + * A GuacamoleConfiguration generated using the information + * provided by the user in the URI. + * + * @throws GuacamoleException + * If an error occurs parsing the URI. + */ + public static GuacamoleConfiguration getConfiguration(String uri) + throws GuacamoleException { + + // Parse the provided String into a URI object. + URI qcUri; + try { + qcUri = new URI(uri); + if (!qcUri.isAbsolute()) + throw new QuickConnectException("URI must be absolute.", + "QUICKCONNECT.ERROR_NOT_ABSOLUTE_URI"); + } + catch (URISyntaxException e) { + throw new QuickConnectException("Invalid URI Syntax", + "QUICKCONNECT.ERROR_INVALID_URI"); + } + + // Break out individual components of the URI. + String protocol = qcUri.getScheme(); + String host = qcUri.getHost(); + int port = qcUri.getPort(); + String userInfo = qcUri.getUserInfo(); + String query = qcUri.getQuery(); + + // Generate a new GuacamoleConfiguration + GuacamoleConfiguration qcConfig = new GuacamoleConfiguration(); + + // Check for protocol and set it, or throw an error if not present + if (protocol != null && !protocol.isEmpty()) + qcConfig.setProtocol(protocol); + else + throw new QuickConnectException("No protocol specified.", + "QUICKCONNECT.ERROR_NO_PROTOCOL"); + + // Check for provided port number + if (port > 0) + qcConfig.setParameter("port", Integer.toString(port)); + + // Check for provided host, or throw an error if not present + if (host != null && !host.isEmpty()) + qcConfig.setParameter("hostname", host); + else + throw new QuickConnectException("No host specified.", + "QUICKCONNECT.ERROR_NO_HOST"); + + // Look for extra query parameters and parse them out. + if (query != null && !query.isEmpty()) { + try { + Map queryParams = parseQueryString(query); + if (queryParams != null) + for (Map.Entry entry: queryParams.entrySet()) + qcConfig.setParameter(entry.getKey(), entry.getValue()); + } + catch (UnsupportedEncodingException e) { + throw new GuacamoleServerException("Unexpected lack of UTF-8 encoding support.", e); + } + } + + // Look for the username and password and parse them out. + if (userInfo != null && !userInfo.isEmpty()) { + + try { + parseUserInfo(userInfo, qcConfig); + } + catch (UnsupportedEncodingException e) { + throw new GuacamoleServerException("Unexpected lack of UTF-8 encoding support.", e); + } + } + + return qcConfig; + + } + + /** + * Parse the given string for parameter key/value pairs and return + * a map with the parameters. + * + * @param queryStr + * The query string to parse for key/value pairs. + * + * @return + * A map with the key/value pairs. + * + * @throws UnsupportedEncodingException + * If Java lacks UTF-8 support. + */ + public static Map parseQueryString(String queryStr) + throws UnsupportedEncodingException { + + // Split the query string into the pairs + List paramList = Arrays.asList(queryStr.split("&")); + Map parameters = new HashMap(); + + // Loop through key/value pairs and put them in the Map. + for (String param : paramList) { + String[] paramArray = param.split("=", 2); + parameters.put(URLDecoder.decode(paramArray[0], "UTF-8"), + URLDecoder.decode(paramArray[1], "UTF-8")); + } + + return parameters; + } + + /** + * Parse the given string for username and password values, + * and, if values are present, decode them and set them in + * the provided GuacamoleConfiguration object. + * + * @param userInfo + * The string to parse for username/password values. + * + * @param config + * The GuacamoleConfiguration object to store the username + * and password in. + * + * @throws UnsupportedEncodingException + * If Java lacks UTF-8 support. + */ + public static void parseUserInfo(String userInfo, + GuacamoleConfiguration config) + throws UnsupportedEncodingException { + + Matcher userinfoMatcher = userinfoPattern.matcher(userInfo); + + if (userinfoMatcher.matches()) { + String username = userinfoMatcher.group(USERNAME_GROUP); + String password = userinfoMatcher.group(PASSWORD_GROUP); + + if (username != null && !username.isEmpty()) + config.setParameter("username", + URLDecoder.decode(username, "UTF-8")); + + if (password != null && !password.isEmpty()) + config.setParameter("password", + URLDecoder.decode(password, "UTF-8")); + } + + } + + /** + * Given a GuacamoleConfiguration object, generate a name + * for the configuration based on the protocol, host, user + * and port in the configuration, and return the string value. + * + * @param config + * The GuacamoleConfiguration object to use to generate + * the name. + * + * @return + * The String value of the name that is generated. + * + * @throws GuacamoleException + * If an error occurs getting items in the configuration. + */ + public static String getName(GuacamoleConfiguration config) + throws GuacamoleException { + + if (config == null) + return null; + + String protocol = config.getProtocol(); + String host = config.getParameter("hostname"); + String port = config.getParameter("port"); + String user = config.getParameter("username"); + + StringBuilder name = new StringBuilder(); + + if (protocol != null && !protocol.isEmpty()) + name.append(protocol).append("://"); + + if (user != null && !user.isEmpty()) + name.append(user).append("@"); + + if (host != null && !host.isEmpty()) + name.append(host); + + if (port != null && !port.isEmpty()) + name.append(":").append(port); + + name.append("/"); + + return name.toString(); + } + +} 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..34c67800c --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/resources/controllers/quickconnectController.js @@ -0,0 +1,59 @@ +/* + * 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', + function manageConnectionController($scope, $injector) { + + // Required types + var ClientIdentifier = $injector.get('ClientIdentifier'); + + // Required services + var $location = $injector.get('$location'); + var guacNotification = $injector.get('guacNotification'); + var quickConnectService = $injector.get('quickConnectService'); + + /** + * The URI that will be passed in to the extension to create + * the connection. + */ + $scope.uri = null; + + /** + * Saves the connection, creating a new connection or updating the existing + * connection. + */ + $scope.quickConnect = function quickConnect() { + + quickConnectService.createConnection($scope.uri) + .then(function createdConnection(connectionId) { + $location.url('/client/' + ClientIdentifier.toString({ + dataSource : 'quickconnect', + type : ClientIdentifier.Types.CONNECTION, + id : connectionId + })); + }, guacNotification.SHOW_REQUEST_ERROR); + + return; + + }; + +}]); 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..62dd3bbfa --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/resources/guac-manifest.json @@ -0,0 +1,31 @@ +{ + "guacamoleVersion" : "0.9.14", + + "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" + ], + + "translations" : [ + "translations/en.json" + ], + + "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..ed397a6ed --- /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 QuickConnect 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/services/quickConnectService.js b/extensions/guacamole-auth-quickconnect/src/main/resources/services/quickConnectService.js new file mode 100644 index 000000000..0da298087 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/resources/services/quickConnectService.js @@ -0,0 +1,69 @@ +/* + * 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. + */ + +/** + * Service for managing quickConnect extension. + */ +angular.module('guacQuickConnect').factory('quickConnectService', ['$injector', + function quickConnectService($injector) { + + // Required services + var authenticationService = $injector.get('authenticationService'); + var cacheService = $injector.get('cacheService'); + var requestService = $injector.get('requestService'); + + var service = {}; + + /** + * Makes a request to the REST API to create a connection, returning a + * promise that can be used for processing the results of the call. + * + * @param {uri} The URI of the connection to create. + * + * @returns {Promise} + * A promise for the HTTP call which will succeed if and only if the + * save operation is successful. + */ + service.createConnection = function createConnection(uri) { + + // Build HTTP parameters set + var httpParameters = { + token : authenticationService.getCurrentToken() + }; + + return requestService({ + method : 'POST', + url : 'api/session/ext/quickconnect/create', + params : httpParameters, + data : $.param({uri: uri}), + headers : {'Content-Type': 'application/x-www-form-urlencoded'} + }) + .then(function connectionCreated(connectionId) { + // Clear connections and users from cache. + cacheService.connections.removeAll(); + cacheService.users.removeAll(); + + // Pass on the connection identifier + return connectionId.identifier; + }); + + }; + + return service; +}]); 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..2f112623c --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/resources/styles/quickconnect.css @@ -0,0 +1,53 @@ +/* + * 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.25em 0; + width: 100%; + margin-left: 0.50em; +} + +.quickconnect-container .quickconnect-field { + background-image: url('images/protocol-icons/guac-text.png'); + background-repeat: no-repeat; + background-size: 1.50em; + background-position: 0.25em center; + background-color: transparent; + padding: 0.25em; + padding-left: 2.50em; + width: 100%; + max-width: none; + border: 0; + box-sizing: border-box; +} + +.quickconnect-button { + clear: both; + float: right; + margin-right: 0.75em; + font-size: 0.75em; + padding: 0.25em; +} + +.quickconnect-list-item { + vertical-align: middle; + align-content: center; + display: flex +} 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..6089965c5 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/resources/templates/quickconnectField.html @@ -0,0 +1,11 @@ + +
+
+
+ +
+
+
+ +
+
diff --git a/extensions/guacamole-auth-quickconnect/src/main/resources/translations/en.json b/extensions/guacamole-auth-quickconnect/src/main/resources/translations/en.json new file mode 100644 index 000000000..50ffb2dff --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/resources/translations/en.json @@ -0,0 +1,18 @@ +{ + + "DATA_SOURCE_QUICKCONNECT" : { + "NAME" : "QuickConnect" + }, + + "QUICKCONNECT" : { + "ACTION_CONNECT" : "Connect", + + "ERROR_INVALID_URI" : "Invalid URI Specified", + "ERROR_NO_HOST" : "No host specified", + "ERROR_NO_PROTOCOL" : "No protocol specified", + "ERROR_NOT_ABSOLUTE_URI" : "URI is not absolute", + + "FIELD_PLACEHOLDER_URI" : "Enter Connection URI" + } + +} diff --git a/extensions/guacamole-auth-quickconnect/src/test/java/org/apache/guacamole/auth/quickconnect/utility/QCParserTest.java b/extensions/guacamole-auth-quickconnect/src/test/java/org/apache/guacamole/auth/quickconnect/utility/QCParserTest.java new file mode 100644 index 000000000..50ad7d6f2 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/test/java/org/apache/guacamole/auth/quickconnect/utility/QCParserTest.java @@ -0,0 +1,122 @@ +/* + * 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.utility; + +import java.io.UnsupportedEncodingException; +import java.util.Map; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.protocol.GuacamoleConfiguration; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * Class to test methods in the QCParser utility class. + */ +public class QCParserTest { + + /** + * Verify that the parseQueryString() method functions as designed. + * + * @throws UnsupportedEncodingException + * If Java lacks UTF-8 support. + */ + @Test + public void testParseQueryString() throws UnsupportedEncodingException { + + final String queryString = "param1=value1¶m2=value2=3¶m3=value%3D3¶m4=value%264"; + Map queryMap = QCParser.parseQueryString(queryString); + + assertEquals("value1", queryMap.get("param1")); + assertEquals("value2=3", queryMap.get("param2")); + assertEquals("value=3", queryMap.get("param3")); + assertEquals("value&4", queryMap.get("param4")); + + } + + /** + * Verify that the parseUserInfo() method functions as designed. + * + * @throws UnsupportedEncodingException + * If Java lacks UTF-8 support. + */ + @Test + public void testParseUserInfo() throws UnsupportedEncodingException { + + Map userInfoMap; + + GuacamoleConfiguration config1 = new GuacamoleConfiguration(); + QCParser.parseUserInfo("guacuser:secretpw", config1); + assertEquals("guacuser", config1.getParameter("username")); + assertEquals("secretpw", config1.getParameter("password")); + + GuacamoleConfiguration config2 = new GuacamoleConfiguration(); + QCParser.parseUserInfo("guacuser", config2); + assertEquals("guacuser", config2.getParameter("username")); + assertNull(config2.getParameter("password")); + + GuacamoleConfiguration config3 = new GuacamoleConfiguration(); + QCParser.parseUserInfo("guacuser:P%40ssw0rd%21", config3); + assertEquals("guacuser", config3.getParameter("username")); + assertEquals("P@ssw0rd!", config3.getParameter("password")); + + GuacamoleConfiguration config4 = new GuacamoleConfiguration(); + QCParser.parseUserInfo("domain%5cguacuser:domain%2fpassword", config4); + assertEquals("domain\\guacuser", config4.getParameter("username")); + assertEquals("domain/password", config4.getParameter("password")); + + } + + /** + * Verify that the getConfiguration() method returns the expected + * GuacamoleConfiguration object. + * + * @throws GuacamoleException + * If the configuration cannot be parsed from the given URI. + */ + @Test + public void testGetConfiguration() throws GuacamoleException { + + String uri1 = "ssh://guacuser:guacpassword@hostname1.domain.local/?param1=value1¶m2=value2"; + GuacamoleConfiguration config1 = QCParser.getConfiguration(uri1); + assertEquals("ssh", config1.getProtocol()); + assertEquals("hostname1.domain.local", config1.getParameter("hostname")); + assertEquals("guacuser", config1.getParameter("username")); + assertEquals("guacpassword", config1.getParameter("password")); + assertEquals("value1", config1.getParameter("param1")); + assertEquals("value2", config1.getParameter("param2")); + + String uri2 = "rdp://domain%5cguacuser:adPassword123@windows1.domain.tld/?enable-sftp=true"; + GuacamoleConfiguration config2 = QCParser.getConfiguration(uri2); + assertEquals("rdp", config2.getProtocol()); + assertEquals("windows1.domain.tld", config2.getParameter("hostname")); + assertEquals("domain\\guacuser", config2.getParameter("username")); + assertEquals("adPassword123", config2.getParameter("password")); + assertEquals("true", config2.getParameter("enable-sftp")); + + String uri3 = "vnc://mirror1.example.com:5910/"; + GuacamoleConfiguration config3 = QCParser.getConfiguration(uri3); + assertEquals("vnc", config3.getProtocol()); + assertEquals("mirror1.example.com", config3.getParameter("hostname")); + assertEquals("5910", config3.getParameter("port")); + + } + +} 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