From 287ab56f0f793190522e9d173f3fbd61c9155919 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Wed, 16 Aug 2017 06:52:10 -0400 Subject: [PATCH 01/16] GUACAMOLE-364: factor out common provider class instantiation support This will allow the same error and debug logging to be used both for the AuthenticationProviderFacade and a new ListenerFacade. --- .../AuthenticationProviderFacade.java | 55 +--------- .../guacamole/extension/ProviderFactory.java | 102 ++++++++++++++++++ 2 files changed, 104 insertions(+), 53 deletions(-) create mode 100644 guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java b/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java index e1ed5ff68..cb65909a0 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java @@ -19,7 +19,6 @@ package org.apache.guacamole.extension; -import java.lang.reflect.InvocationTargetException; import java.util.UUID; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.AuthenticatedUser; @@ -66,58 +65,8 @@ public class AuthenticationProviderFacade implements AuthenticationProvider { * The AuthenticationProvider subclass to instantiate. */ public AuthenticationProviderFacade(Class authProviderClass) { - - AuthenticationProvider instance = null; - - try { - // Attempt to instantiate the authentication provider - instance = authProviderClass.getConstructor().newInstance(); - } - catch (NoSuchMethodException e) { - logger.error("The authentication extension in use is not properly defined. " - + "Please contact the developers of the extension or, if you " - + "are the developer, turn on debug-level logging."); - logger.debug("AuthenticationProvider is missing a default constructor.", e); - } - catch (SecurityException e) { - logger.error("The Java security mananager is preventing authentication extensions " - + "from being loaded. Please check the configuration of Java or your " - + "servlet container."); - logger.debug("Creation of AuthenticationProvider disallowed by security manager.", e); - } - catch (InstantiationException e) { - logger.error("The authentication extension in use is not properly defined. " - + "Please contact the developers of the extension or, if you " - + "are the developer, turn on debug-level logging."); - logger.debug("AuthenticationProvider cannot be instantiated.", e); - } - catch (IllegalAccessException e) { - logger.error("The authentication extension in use is not properly defined. " - + "Please contact the developers of the extension or, if you " - + "are the developer, turn on debug-level logging."); - logger.debug("Default constructor of AuthenticationProvider is not public.", e); - } - catch (IllegalArgumentException e) { - logger.error("The authentication extension in use is not properly defined. " - + "Please contact the developers of the extension or, if you " - + "are the developer, turn on debug-level logging."); - logger.debug("Default constructor of AuthenticationProvider cannot accept zero arguments.", e); - } - catch (InvocationTargetException e) { - - // Obtain causing error - create relatively-informative stub error if cause is unknown - Throwable cause = e.getCause(); - if (cause == null) - cause = new GuacamoleException("Error encountered during initialization."); - - logger.error("Authentication extension failed to start: {}", cause.getMessage()); - logger.debug("AuthenticationProvider instantiation failed.", e); - - } - - // Associate instance, if any - authProvider = instance; - + authProvider = ProviderFactory.newInstance("authentication provider", + authProviderClass); } @Override diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java b/guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java new file mode 100644 index 000000000..0620b4da8 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java @@ -0,0 +1,102 @@ +/* + * 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.extension; + +import org.apache.guacamole.GuacamoleException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.InvocationTargetException; + +/** + * Static factory method for creating provider instances and logging unexpected outcomes + * with sufficient detail to allow user/developer debugging. + */ +class ProviderFactory { + + private static final Logger logger = LoggerFactory.getLogger(ProviderFactory.class); + + /** + * Creates an instance of the specified provider class using the no-arg constructor. + * + * @param typeName + * The provider type name used for log messages (e.g. "authentication provider") + * @param providerClass + * The provider class to instantiate + * @param + * The provider type + * @return + * A provider instance or null if no instance was created due to error + */ + static T newInstance(String typeName, Class providerClass) { + T instance = null; + + try { + // Attempt to instantiate the provider + instance = providerClass.getConstructor().newInstance(); + } + catch (NoSuchMethodException e) { + logger.error("The {} extension in use is not properly defined. " + + "Please contact the developers of the extension or, if you " + + "are the developer, turn on debug-level logging.", typeName); + logger.debug("{} is missing a default constructor.", + providerClass.getName(), e); + } + catch (SecurityException e) { + logger.error("The Java security manager is preventing extensions " + + "from being loaded. Please check the configuration of Java or your " + + "servlet container."); + logger.debug("Creation of {} disallowed by security manager.", + providerClass.getName(), e); + } + catch (InstantiationException e) { + logger.error("The {} extension in use is not properly defined. " + + "Please contact the developers of the extension or, if you " + + "are the developer, turn on debug-level logging.", typeName); + logger.debug("{} cannot be instantiated.", providerClass.getName(), e); + } + catch (IllegalAccessException e) { + logger.error("The {} extension in use is not properly defined. " + + "Please contact the developers of the extension or, if you " + + "are the developer, turn on debug-level logging."); + logger.debug("Default constructor of {} is not public.", typeName, e); + } + catch (IllegalArgumentException e) { + logger.error("The {} extension in use is not properly defined. " + + "Please contact the developers of the extension or, if you " + + "are the developer, turn on debug-level logging.", typeName); + logger.debug("Default constructor of {} cannot accept zero arguments.", + providerClass.getName(), e); + } + catch (InvocationTargetException e) { + // Obtain causing error - create relatively-informative stub error if cause is unknown + Throwable cause = e.getCause(); + if (cause == null) + cause = new GuacamoleException("Error encountered during initialization."); + + logger.error("{} extension failed to start: {}", typeName, cause.getMessage()); + logger.debug("{} instantiation failed.", providerClass.getName(), e); + } + + return instance; + } + +} From 6f89a0b53047dd1cc9fac8264639c30067bf6cb6 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Wed, 16 Aug 2017 06:54:16 -0400 Subject: [PATCH 02/16] GUACAMOLE-364: listener interfaces now extend a common marker interface --- .../AuthenticationFailureListener.java | 3 +- .../AuthenticationSuccessListener.java | 2 +- .../net/event/listener/Listener.java | 29 +++++++++++++++++++ .../event/listener/TunnelCloseListener.java | 4 +-- .../event/listener/TunnelConnectListener.java | 2 +- 5 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java index 5fcd27b67..7fa619042 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java @@ -27,8 +27,7 @@ import org.apache.guacamole.net.event.AuthenticationFailureEvent; * after a user's authentication attempt fails. Note that this hook cannot * be used to cancel the authentication failure. */ -public interface AuthenticationFailureListener { - +public interface AuthenticationFailureListener extends Listener { /** * Event hook which fires immediately after a user's authentication attempt * fails. diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java index 7db072c36..cc5e01d57 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java @@ -28,7 +28,7 @@ import org.apache.guacamole.net.event.AuthenticationSuccessEvent; * the authenticationSucceeded() hook has the opportunity to cancel the * authentication and force it to fail. */ -public interface AuthenticationSuccessListener { +public interface AuthenticationSuccessListener extends Listener { /** * Event hook which fires immediately after a user's authentication attempt diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java new file mode 100644 index 000000000..72f859db1 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.guacamole.net.event.listener; + +/** + * A marker interface extended by all listener types. This interface is used + * simply to validate that a listener class identified in an extension + * actually implements some listener interface. + */ +public interface Listener { +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java index 784e4e932..99f2c04de 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java @@ -26,10 +26,10 @@ import org.apache.guacamole.net.event.TunnelCloseEvent; * A listener whose tunnelClosed() hook will fire immediately after an * existing tunnel is closed. */ -public interface TunnelCloseListener { +public interface TunnelCloseListener extends Listener { /** - * Event hook which fires immediately after an existing tunnel is closed. + * Event hook which fires immediately before an existing tunnel is closed. * The return value of this hook dictates whether the tunnel is allowed to * be closed. * diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java index da14fe277..7ac47e189 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java @@ -26,7 +26,7 @@ import org.apache.guacamole.net.event.TunnelConnectEvent; * A listener whose tunnelConnected() hook will fire immediately after a new * tunnel is connected. */ -public interface TunnelConnectListener { +public interface TunnelConnectListener extends Listener { /** * Event hook which fires immediately after a new tunnel is connected. From dca7862351fd4c27e82ecaf1cf540180501f48ec Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Wed, 16 Aug 2017 06:54:55 -0400 Subject: [PATCH 03/16] GUACAMOLE-364: add facade used to wrap extension listeners --- .../guacamole/extension/ListenerFacade.java | 135 ++++++++++++++++++ .../guacamole/extension/ListenerProvider.java | 37 +++++ 2 files changed, 172 insertions(+) create mode 100644 guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java create mode 100644 guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java new file mode 100644 index 000000000..278b2a934 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java @@ -0,0 +1,135 @@ +/* + * 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.extension; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.event.AuthenticationFailureEvent; +import org.apache.guacamole.net.event.AuthenticationSuccessEvent; +import org.apache.guacamole.net.event.TunnelCloseEvent; +import org.apache.guacamole.net.event.TunnelConnectEvent; +import org.apache.guacamole.net.event.listener.*; + +/** + * Provides a wrapper around a Listener subclass, allowing listener + * extensions to be bound without regard for which specific listener interfaces + * are implemented. + */ +class ListenerFacade implements ListenerProvider { + + private final Listener delegate; + + /** + * Creates a new ListenerFacade which delegates all listener methods + * calls to an instance of the given Listener subclass. If + * an instance of the given class cannot be created, creation of this + * facade will still succeed. Errors will be logged at the time listener + * creation fails, but subsequent events directed to the listener will be + * silently dropped. + * + * @param listenerClass + * The Listener subclass to instantiate. + */ + public ListenerFacade(Class listenerClass) { + delegate = ProviderFactory.newInstance("listener", listenerClass); + } + + /** + * Notifies the delegate listener of an authentication success event, if the + * listener implements the AuthenticationSuccessListener interface. + * + * @param + * e The AuthenticationSuccessEvent describing the authentication + * success that just occurred. + * @return + * false if the delegate listener rejects the successful authentication, + * else true + * + * @throws GuacamoleException + * if the delegate listener throws this exception + */ + @Override + public boolean authenticationSucceeded(AuthenticationSuccessEvent e) + throws GuacamoleException { + return !(delegate instanceof AuthenticationSuccessListener) + || ((AuthenticationSuccessListener) delegate).authenticationSucceeded(e); + } + + /** + * Notifies the delegate listener of an authentication failure event, if the + * listener implements the AuthenticationSuccessListener interface. + * + * @param + * e The AuthenticationFailureEvent describing the authentication + * failure that just occurred. + * + * @throws GuacamoleException + * if the delegate listener throws this exception + */ + @Override + public void authenticationFailed(AuthenticationFailureEvent e) + throws GuacamoleException { + if (delegate instanceof AuthenticationFailureListener) { + ((AuthenticationFailureListener) delegate).authenticationFailed(e); + } + } + + /** + * Notifies the delegate listener of a tunnel connected event, if the + * listener implements the TunnelConnectListener interface. + * + * @param + * e The TunnelConnectEvent describing the tunnel that was just connected + + * @return + * false if the delegate listener rejects the tunnel connection, + * else true + * + * @throws GuacamoleException + * if the delegate listener throws this exception + */ + @Override + public boolean tunnelConnected(TunnelConnectEvent e) + throws GuacamoleException { + return !(delegate instanceof TunnelConnectListener) + || ((TunnelConnectListener) delegate).tunnelConnected(e); + } + + /** + * Notifies the delegate listener of a tunnel close event, if the + * listener implements the TunnelCloseListener interface. + * + * @param + * e The TunnelCloseEvent describing the tunnel that is to be close + + * @return + * false if the delegate listener rejects the tunnel close request, + * else true + * + * @throws GuacamoleException + * if the delegate listener throws this exception + */ + @Override + public boolean tunnelClosed(TunnelCloseEvent e) throws GuacamoleException { + return !(delegate instanceof TunnelCloseListener) + || ((TunnelCloseListener) delegate).tunnelClosed(e); + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java new file mode 100644 index 000000000..0b3a7473d --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java @@ -0,0 +1,37 @@ +/* + * 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.extension; + +import org.apache.guacamole.net.event.listener.AuthenticationFailureListener; +import org.apache.guacamole.net.event.listener.AuthenticationSuccessListener; +import org.apache.guacamole.net.event.listener.TunnelCloseListener; +import org.apache.guacamole.net.event.listener.TunnelConnectListener; + +/** + * A provider of an event listener. While an implementation of this interface + * must implement all of the specified listener interfaces, an implementation + * may selectively deliver event notifications to an underlying delegate based + * on the specific listener interfaces implemented by the delegate. + */ +public interface ListenerProvider extends AuthenticationSuccessListener, + AuthenticationFailureListener, TunnelConnectListener, + TunnelCloseListener { +} From 109d57ecb372c3fc8114792086f339e9269eaf98 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Wed, 16 Aug 2017 06:55:28 -0400 Subject: [PATCH 04/16] GUACAMOLE-364: add extension module support for event listeners --- .../apache/guacamole/extension/Extension.java | 95 +++++++++++++++++++ .../extension/ExtensionManifest.java | 31 ++++++ .../guacamole/extension/ExtensionModule.java | 53 +++++++++++ 3 files changed, 179 insertions(+) diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java b/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java index 3183fa21c..ac58676ec 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java @@ -35,6 +35,8 @@ import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; + +import org.apache.guacamole.net.event.listener.Listener; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.map.ObjectMapper; import org.apache.guacamole.GuacamoleException; @@ -109,6 +111,11 @@ public class Extension { */ private final Collection> authenticationProviderClasses; + /** + * The collection of all Listener classes defined within the extension. + */ + private final Collection> listenerClasses; + /** * The resource for the small favicon for the extension. If provided, this * will replace the default Guacamole icon. @@ -265,6 +272,80 @@ public class Extension { } + /** + * Retrieve the Listener subclass having the given name. If + * the class having the given name does not exist or isn't actually a + * subclass of Listener, an exception will be thrown. + * + * @param name + * The name of the Listener class to retrieve. + * + * @return + * The subclass of Listener having the given name. + * + * @throws GuacamoleException + * If no such class exists, or if the class with the given name is not + * a subclass of Listener. + */ + @SuppressWarnings("unchecked") // We check this ourselves with isAssignableFrom() + private Class getListenerClass(String name) + throws GuacamoleException { + + try { + + // Get listener class + Class listenerClass = classLoader.loadClass(name); + + // Verify the located class is actually a subclass of Listener + if (!Listener.class.isAssignableFrom(listenerClass)) + throw new GuacamoleServerException("Listeners MUST implement a Listener subclass."); + + // Return located class + return (Class) listenerClass; + + } + catch (ClassNotFoundException e) { + throw new GuacamoleException("Listener class not found.", e); + } + catch (LinkageError e) { + throw new GuacamoleException("Listener class cannot be loaded (wrong version of API?).", e); + } + + } + + /** + * Returns a new collection of all Listener subclasses having the given names. + * If any class does not exist or isn't actually subclass of Listener, an + * exception will be thrown, an no further Listener classes will be loaded. + * + * @param names + * The names of the AuthenticationProvider classes to retrieve. + * + * @return + * A new collection of all AuthenticationProvider subclasses having the + * given names. + * + * @throws GuacamoleException + * If any given class does not exist, or if any given class is not a + * subclass of AuthenticationProvider. + */ + private Collection> getListenerClasses(Collection names) + throws GuacamoleException { + + // If no classnames are provided, just return an empty list + if (names == null) + return Collections.>emptyList(); + + // Define all auth provider classes + Collection> classes = new ArrayList>(names.size()); + for (String name : names) + classes.add(getListenerClass(name)); + + // Callers should not rely on modifying the result + return Collections.unmodifiableCollection(classes); + } + + /** * Loads the given file as an extension, which must be a .jar containing * a guac-manifest.json file describing its contents. @@ -363,6 +444,9 @@ public class Extension { // Define authentication providers authenticationProviderClasses = getAuthenticationProviderClasses(manifest.getAuthProviders()); + // Define listeners + listenerClasses = getListenerClasses(manifest.getListeners()); + // Get small icon resource if provided if (manifest.getSmallIcon() != null) smallIcon = new ClassPathResource(classLoader, "image/png", manifest.getSmallIcon()); @@ -488,6 +572,17 @@ public class Extension { return authenticationProviderClasses; } + /** + * Returns all declared listener classes associated wit this extension. Listeners are + * declared within the extension manifest. + * + * @return + * All declared listener classes with this extension. + */ + public Collection> getListenerClasses() { + return listenerClasses; + } + /** * Returns the resource for the small favicon for the extension. If * provided, this will replace the default Guacamole icon. diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java index 9b9bd9bee..1636b0350 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java @@ -87,6 +87,11 @@ public class ExtensionManifest { */ private Collection authProviders; + /** + * The names of all listener classes within this extension, if any. + */ + private Collection listeners; + /** * The path to the small favicon. If provided, this will replace the default * Guacamole icon. @@ -355,6 +360,32 @@ public class ExtensionManifest { this.authProviders = authProviders; } + /** + * Returns the classnames of all listener classes within the extension. + * These classnames are defined within the manifest by the "listeners" + * property as an array of strings, where each string is a listener + * class name. + * + * @return + * a collection of classnames for all listeners within the extension + */ + public Collection getListeners() { + return listeners; + } + + /** + * Sets the classnames of all listener classes within the extension. + * These classnames are defined within the manifest by the "listeners" + * property as an array of strings, where each string is a listener + * class name. + * + * @param listeners + * a collection of classnames for all listeners within the extension + */ + public void setListeners(Collection listeners) { + this.listeners = listeners; + } + /** * Returns the path to the small favicon, relative to the root of the * extension. diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java index 792066c87..1e1a85452 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java @@ -34,6 +34,7 @@ import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.environment.Environment; import org.apache.guacamole.net.auth.AuthenticationProvider; +import org.apache.guacamole.net.event.listener.Listener; import org.apache.guacamole.resource.Resource; import org.apache.guacamole.resource.ResourceServlet; import org.apache.guacamole.resource.SequenceResource; @@ -91,6 +92,12 @@ public class ExtensionModule extends ServletModule { private final List boundAuthenticationProviders = new ArrayList(); + /** + * All currently-bound authentication providers, if any. + */ + private final List boundListenerProviders = + new ArrayList(); + /** * Service for adding and retrieving language resources. */ @@ -187,6 +194,49 @@ public class ExtensionModule extends ServletModule { return Collections.unmodifiableList(boundAuthenticationProviders); } + /** + * Binds the given Listener class such that any service + * requiring access to the Listener can obtain it via + * injection, along with any other bound Listener. + * + * @param listenerClass + * The Listener class to bind. + */ + private void bindListenerProvider(Class listenerClass) { + + // Bind listener + logger.debug("[{}] Binding Listener \"{}\".", + boundListenerProviders.size(), listenerClass.getName()); + boundListenerProviders.add(new ListenerFacade(listenerClass)); + } + + /** + * Binds each of the the given Listener classes such that any + * service requiring access to the Listener can obtain it via + * injection. + * + * @param listeners + * The Listener classes to bind. + */ + private void bindListenerProviders(Collection> listeners) { + + // Bind each listener within extension + for (Class listener : listeners) + bindListenerProvider(listener); + } + + /** + * Returns a list of all currently-bound ListenerProvider instances. + * + * @return + * A List of all currently-bound ListenerProvider instances. The List is + * not modifiable. + */ + @Provides + public List getListenerProviders() { + return Collections.unmodifiableList(boundListenerProviders); + } + /** * Serves each of the given resources as a language resource. Language * resources are served from within the "/translations" directory as JSON @@ -327,6 +377,9 @@ public class ExtensionModule extends ServletModule { // Attempt to load all authentication providers bindAuthenticationProviders(extension.getAuthenticationProviderClasses()); + // Attempt to load all listeners + bindListenerProviders(extension.getListenerClasses()); + // Add any translation resources serveLanguageResources(extension.getTranslationResources()); From cfb879b763b4254ba1c44b38d7fb5993b3e62187 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Wed, 16 Aug 2017 06:57:16 -0400 Subject: [PATCH 05/16] GUACAMOLE-364: add injectable ListenerService --- .../guacamole/rest/RESTServiceModule.java | 2 + .../guacamole/rest/event/ListenerService.java | 142 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java index cab4d973b..587d8338e 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java @@ -19,6 +19,7 @@ package org.apache.guacamole.rest; +import org.apache.guacamole.rest.event.ListenerService; import org.apache.guacamole.rest.session.UserContextResourceFactory; import org.apache.guacamole.rest.session.SessionRESTService; import com.google.inject.Scopes; @@ -76,6 +77,7 @@ public class RESTServiceModule extends ServletModule { bind(TokenSessionMap.class).toInstance(tokenSessionMap); // Bind low-level services + bind(ListenerService.class); bind(AuthenticationService.class); bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java b/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java new file mode 100644 index 000000000..3b1a00f56 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java @@ -0,0 +1,142 @@ +/* + * 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.rest.event; + +import com.google.inject.Inject; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.extension.ListenerProvider; +import org.apache.guacamole.net.event.AuthenticationFailureEvent; +import org.apache.guacamole.net.event.AuthenticationSuccessEvent; +import org.apache.guacamole.net.event.TunnelCloseEvent; +import org.apache.guacamole.net.event.TunnelConnectEvent; +import org.apache.guacamole.net.event.listener.AuthenticationFailureListener; +import org.apache.guacamole.net.event.listener.AuthenticationSuccessListener; +import org.apache.guacamole.net.event.listener.TunnelCloseListener; +import org.apache.guacamole.net.event.listener.TunnelConnectListener; + +import java.util.List; + +/** + * A service used to notify listeners registered by extensions when events of + * interest occur. + * + * @author Carl Harris + */ +public class ListenerService implements ListenerProvider { + + @Inject + private List listeners; + + /** + * Notifies all bound listeners of an authentication success event. Listeners + * are allowed to veto a successful authentication by returning false from the + * listener method. Regardless of whether a particular listener rejects the + * successful authentication, all listeners are notified. + * @param e + * The AuthenticationSuccessEvent describing the successful authentication + * that just occurred. + * + * @return + * false if any bound listener returns false, else true + * + * @throws GuacamoleException + * If any bound listener throws this exception. If a listener throws an exception + * some listeners may not receive the authentication success event notification. + */ + @Override + public boolean authenticationSucceeded(AuthenticationSuccessEvent e) + throws GuacamoleException { + boolean result = true; + for (AuthenticationSuccessListener listener : listeners) { + result = result && listener.authenticationSucceeded(e); + } + return result; + } + + /** + * Notifies all bound listeners of an authentication failure event. + * + * @param e + * The AuthenticationSuccessEvent describing the authentication failure + * that just occurred. + * + * @throws GuacamoleException + * If any bound listener throws this exception. If a listener throws an exception + * some listeners may not receive the authentication failure event notification. + */ + @Override + public void authenticationFailed(AuthenticationFailureEvent e) + throws GuacamoleException { + for (AuthenticationFailureListener listener : listeners) { + listener.authenticationFailed(e); + } + } + + /** + * Notifies all bound listeners of an tunnel connected event. Listeners + * are allowed to veto a tunnel connection by returning false from the + * listener method. Regardless of whether a particular listener rejects the + * tunnel connection, all listeners are notified. + * @param e + * The TunnelConnectedEvent describing the tunnel that was just connected + * + * @return + * false if any bound listener returns false, else true + * + * @throws GuacamoleException + * If any bound listener throws this exception. If a listener throws an exception + * some listeners may not receive the tunnel connected event notification. + */ + @Override + public boolean tunnelConnected(TunnelConnectEvent e) + throws GuacamoleException { + boolean result = true; + for (TunnelConnectListener listener : listeners) { + result = result && listener.tunnelConnected(e); + } + return result; + } + + /** + * Notifies all bound listeners of an tunnel close event. Listeners + * are allowed to veto the request to close a tunnel by returning false from + * the listener method. Regardless of whether a particular listener rejects the + * tunnel close request, all listeners are notified. + * @param e + * The TunnelCloseEvent describing the tunnel that is to be closed + * + * @return + * false if any bound listener returns false, else true + * + * @throws GuacamoleException + * If any bound listener throws this exception. If a listener throws an exception + * some listeners may not receive the tunnel close event notification. + */ + @Override + public boolean tunnelClosed(TunnelCloseEvent e) throws GuacamoleException { + boolean result = true; + for (TunnelCloseListener listener : listeners) { + result = result && listener.tunnelClosed(e); + } + return result; + } + +} From 5a232f6825deb9b73f89473f941d92d012e36f67 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Wed, 16 Aug 2017 06:58:18 -0400 Subject: [PATCH 06/16] GUACAMOLE-364: notify authentication listeners in AuthenticationService --- ...camoleAuthenticationRejectedException.java | 34 +++++++++ .../rest/auth/AuthenticationService.java | 70 +++++++++++++++++-- 2 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java diff --git a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java new file mode 100644 index 000000000..7d7bfa870 --- /dev/null +++ b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java @@ -0,0 +1,34 @@ +/* + * 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; + +/** + * An exception thrown when a successful authentication is rejected by a + * AuthenticationSuccessListener in an extension. + */ +public class GuacamoleAuthenticationRejectedException + extends GuacamoleSecurityException { + + public GuacamoleAuthenticationRejectedException() { + super("authentication rejected by listener extension"); + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java index 31abee5f5..b6bf5b20e 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java @@ -24,9 +24,11 @@ import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; +import org.apache.guacamole.GuacamoleAuthenticationRejectedException; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleUnauthorizedException; +import org.apache.guacamole.GuacamoleSession; import org.apache.guacamole.environment.Environment; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; @@ -35,7 +37,9 @@ import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.GuacamoleCredentialsException; import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; -import org.apache.guacamole.GuacamoleSession; +import org.apache.guacamole.net.event.AuthenticationFailureEvent; +import org.apache.guacamole.net.event.AuthenticationSuccessEvent; +import org.apache.guacamole.rest.event.ListenerService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,6 +78,12 @@ public class AuthenticationService { @Inject private AuthTokenGenerator authTokenGenerator; + /** + * The service to use to notify registered authentication listeners + */ + @Inject + private ListenerService listenerService; + /** * Regular expression which matches any IPv4 address. */ @@ -207,6 +217,50 @@ public class AuthenticationService { } + /** + * Notify all bound AuthenticationSuccessListeners that a successful authentication + * has occurred. If any of the bound listeners returns false (indicating that the + * authentication should be rejected) a GuacamoleRejectedAuthenticationException is + * thrown. + * + * @param authenticatedUser + * The user that was successfully authenticated + * @param session + * Existing session for the user (if any) + * @throws GuacamoleException + * If a filter throws an exception or if any filter rejects the authentication + */ + private void notifyAuthenticationSuccessListeners( + AuthenticatedUser authenticatedUser, GuacamoleSession session) + throws GuacamoleException { + UserContext userContext = null; + if (session != null) { + userContext = session.getUserContext( + authenticatedUser.getAuthenticationProvider().getIdentifier()); + } + + AuthenticationSuccessEvent event = new AuthenticationSuccessEvent( + userContext, authenticatedUser.getCredentials()); + + boolean ok = listenerService.authenticationSucceeded(event); + if (!ok) { + throw new GuacamoleAuthenticationRejectedException(); + } + } + + /** + * Notify all bound AuthenticationFailureListeners that an authentication has failed. + * + * @param credentials + * The credentials that failed to authenticate + * @throws GuacamoleException + * If a filter throws an exception + */ + private void notifyAuthenticationFailureListeners(Credentials credentials) + throws GuacamoleException { + listenerService.authenticationFailed(new AuthenticationFailureEvent(credentials)); + } + /** * Returns the AuthenticatedUser associated with the given session and * credentials, performing a fresh authentication and creating a new @@ -232,11 +286,17 @@ public class AuthenticationService { try { // Re-authenticate user if session exists - if (existingSession != null) - return updateAuthenticatedUser(existingSession.getAuthenticatedUser(), credentials); + if (existingSession != null) { + AuthenticatedUser updatedUser = updateAuthenticatedUser( + existingSession.getAuthenticatedUser(), credentials); + notifyAuthenticationSuccessListeners(updatedUser, existingSession); + return updatedUser; + } // Otherwise, attempt authentication as a new user - AuthenticatedUser authenticatedUser = AuthenticationService.this.authenticateUser(credentials); + AuthenticatedUser authenticatedUser = authenticateUser(credentials); + notifyAuthenticationSuccessListeners(authenticatedUser, null); + if (logger.isInfoEnabled()) logger.info("User \"{}\" successfully authenticated from {}.", authenticatedUser.getIdentifier(), @@ -249,6 +309,8 @@ public class AuthenticationService { // Log and rethrow any authentication errors catch (GuacamoleException e) { + notifyAuthenticationFailureListeners(credentials); + // Get request and username for sake of logging HttpServletRequest request = credentials.getRequest(); String username = credentials.getUsername(); From 6b6340ac464e03cc2a7bb8e9f72f8044e79beed6 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Wed, 16 Aug 2017 06:59:12 -0400 Subject: [PATCH 07/16] GUACAMOLE-364: notify tunnel listeners in TunnelRequestService --- .../GuacamoleTunnelConnectedException.java | 33 ++++++++ .../GuacamoleTunnelRejectedException.java | 33 ++++++++ .../tunnel/TunnelRequestService.java | 84 ++++++++++++++++++- 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java create mode 100644 guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java diff --git a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java new file mode 100644 index 000000000..96693a483 --- /dev/null +++ b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * An exception thrown when a request to close a tunnel is vetoed by a + * TunnelCloseListener in an extension. + */ +public class GuacamoleTunnelConnectedException extends GuacamoleClientException { + + public GuacamoleTunnelConnectedException() { + super("tunnel close vetoed by listener extension"); + } + +} diff --git a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java new file mode 100644 index 000000000..c2e09f927 --- /dev/null +++ b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * An exception thrown when a successful tunnel connection is rejected by a + * TunnelConnectListener in an extension. + */ +public class GuacamoleTunnelRejectedException extends GuacamoleClientException { + + public GuacamoleTunnelRejectedException() { + super("tunnel connection rejected by listener extension"); + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java index 628386916..823ac1b9f 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java @@ -25,14 +25,20 @@ import java.util.List; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleSession; +import org.apache.guacamole.GuacamoleTunnelConnectedException; +import org.apache.guacamole.GuacamoleTunnelRejectedException; import org.apache.guacamole.GuacamoleUnauthorizedException; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.ConnectionGroup; +import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Directory; import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.net.event.TunnelCloseEvent; +import org.apache.guacamole.net.event.TunnelConnectEvent; import org.apache.guacamole.rest.auth.AuthenticationService; import org.apache.guacamole.protocol.GuacamoleClientInformation; +import org.apache.guacamole.rest.event.ListenerService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,6 +63,74 @@ public class TunnelRequestService { @Inject private AuthenticationService authenticationService; + /** + * A service for notifying listeners about tunnel connect/closed events. + */ + @Inject + private ListenerService listenerService; + + /** + * Notifies bound TunnelConnectListeners that a new tunnel has been connected. + * Listeners are allowed to veto a connected tunnel by returning false from the + * listener method. If the ListenerService indicates that any listener rejected + * the tunnel, the tunnel is closed an GuacamoleTunnelRejectedException is thrown. + * + * @param userContext + * The UserContext associated with the user for whom the tunnel is + * being created. + * + * @param credentials + * Credentials that authenticate the user + * + * @param tunnel + * The tunnel that was connected + * + * @throws GuacamoleException + * If thrown by a listener or if any listener vetoes the connected tunnel + */ + private void notifyTunnelConnectListeners(UserContext userContext, + Credentials credentials, GuacamoleTunnel tunnel) throws GuacamoleException { + TunnelConnectEvent event = new TunnelConnectEvent(userContext, credentials, tunnel); + boolean ok = listenerService.tunnelConnected(event); + if (!ok) { + try { + tunnel.close(); + } + catch (GuacamoleException closeEx) { + logger.warn("Error closing rejected tunnel connection: {}", closeEx.getMessage()); + } + throw new GuacamoleTunnelRejectedException(); + } + } + + /** + * Notifies bound TunnelCloseListeners that a tunnel is to be closed. + * Listeners are allowed to veto a request to close a tunnel by returning false from + * the listener method. If the ListenerService indicates that any listener vetoed the + * request to the close the tunnel, a GuacamoleTunnelConnectedException is thrown. + * + * @param userContext + * The UserContext associated with the user for whom the tunnel is + * being created. + * + * @param credentials + * Credentials that authenticate the user + * + * @param tunnel + * The tunnel that was connected + * + * @throws GuacamoleException + * If thrown by a listener or if any listener vetoes the request to close the tunnel + */ + private void notifyTunnelCloseListeners(UserContext userContext, + Credentials credentials, GuacamoleTunnel tunnel) + throws GuacamoleException { + TunnelCloseEvent event = new TunnelCloseEvent(userContext, credentials, tunnel); + if (listenerService.tunnelClosed(event)) { + throw new GuacamoleTunnelConnectedException(); + } + } + /** * Reads and returns the client information provided within the given * request. @@ -226,7 +300,7 @@ public class TunnelRequestService { * @throws GuacamoleException * If an error occurs while obtaining the tunnel. */ - protected GuacamoleTunnel createAssociatedTunnel(GuacamoleTunnel tunnel, + protected GuacamoleTunnel createAssociatedTunnel(final GuacamoleTunnel tunnel, final String authToken, final GuacamoleSession session, final UserContext context, final TunnelRequest.Type type, final String id) throws GuacamoleException { @@ -243,6 +317,10 @@ public class TunnelRequestService { @Override public void close() throws GuacamoleException { + // notify listeners to allow close request to be vetoed + notifyTunnelCloseListeners(context, + session.getAuthenticatedUser().getCredentials(), tunnel); + long connectionEndTime = System.currentTimeMillis(); long duration = connectionEndTime - connectionStartTime; @@ -328,6 +406,10 @@ public class TunnelRequestService { // Create connected tunnel using provided connection ID and client information GuacamoleTunnel tunnel = createConnectedTunnel(userContext, type, id, info); + // Notify listeners to allow connection to be vetoed + notifyTunnelConnectListeners(userContext, + session.getAuthenticatedUser().getCredentials(), tunnel); + // Associate tunnel with session return createAssociatedTunnel(tunnel, authToken, session, userContext, type, id); From 059cb75f0fd47b8a4c02491710e83695f26456bf Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Thu, 17 Aug 2017 05:10:32 -0400 Subject: [PATCH 08/16] GUACAMOLE-364: address style consistency issues --- ...camoleAuthenticationRejectedException.java | 1 - .../GuacamoleTunnelConnectedException.java | 1 - .../GuacamoleTunnelRejectedException.java | 1 - .../AuthenticationFailureListener.java | 1 + .../net/event/listener/Listener.java | 1 - .../apache/guacamole/extension/Extension.java | 1 - .../guacamole/extension/ListenerFacade.java | 25 +++++++------- .../guacamole/extension/ListenerProvider.java | 1 - .../guacamole/extension/ProviderFactory.java | 1 - .../rest/auth/AuthenticationService.java | 2 ++ .../guacamole/rest/event/ListenerService.java | 33 +++++++++---------- 11 files changed, 30 insertions(+), 38 deletions(-) diff --git a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java index 7d7bfa870..907e0c947 100644 --- a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java +++ b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java @@ -15,7 +15,6 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * */ package org.apache.guacamole; diff --git a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java index 96693a483..c7d21cf1d 100644 --- a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java +++ b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java @@ -15,7 +15,6 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * */ package org.apache.guacamole; diff --git a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java index c2e09f927..b9367c180 100644 --- a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java +++ b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java @@ -15,7 +15,6 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * */ package org.apache.guacamole; diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java index 7fa619042..86122bad2 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java @@ -28,6 +28,7 @@ import org.apache.guacamole.net.event.AuthenticationFailureEvent; * be used to cancel the authentication failure. */ public interface AuthenticationFailureListener extends Listener { + /** * Event hook which fires immediately after a user's authentication attempt * fails. diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java index 72f859db1..58499934e 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java @@ -15,7 +15,6 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * */ package org.apache.guacamole.net.event.listener; diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java b/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java index ac58676ec..697002d42 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java @@ -35,7 +35,6 @@ import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; - import org.apache.guacamole.net.event.listener.Listener; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.map.ObjectMapper; diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java index 278b2a934..ab0b7c85d 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java @@ -15,7 +15,6 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * */ package org.apache.guacamole.extension; @@ -56,7 +55,7 @@ class ListenerFacade implements ListenerProvider { * listener implements the AuthenticationSuccessListener interface. * * @param - * e The AuthenticationSuccessEvent describing the authentication + * event The AuthenticationSuccessEvent describing the authentication * success that just occurred. * @return * false if the delegate listener rejects the successful authentication, @@ -66,10 +65,10 @@ class ListenerFacade implements ListenerProvider { * if the delegate listener throws this exception */ @Override - public boolean authenticationSucceeded(AuthenticationSuccessEvent e) + public boolean authenticationSucceeded(AuthenticationSuccessEvent event) throws GuacamoleException { return !(delegate instanceof AuthenticationSuccessListener) - || ((AuthenticationSuccessListener) delegate).authenticationSucceeded(e); + || ((AuthenticationSuccessListener) delegate).authenticationSucceeded(event); } /** @@ -77,17 +76,17 @@ class ListenerFacade implements ListenerProvider { * listener implements the AuthenticationSuccessListener interface. * * @param - * e The AuthenticationFailureEvent describing the authentication + * event The AuthenticationFailureEvent describing the authentication * failure that just occurred. * * @throws GuacamoleException * if the delegate listener throws this exception */ @Override - public void authenticationFailed(AuthenticationFailureEvent e) + public void authenticationFailed(AuthenticationFailureEvent event) throws GuacamoleException { if (delegate instanceof AuthenticationFailureListener) { - ((AuthenticationFailureListener) delegate).authenticationFailed(e); + ((AuthenticationFailureListener) delegate).authenticationFailed(event); } } @@ -96,7 +95,7 @@ class ListenerFacade implements ListenerProvider { * listener implements the TunnelConnectListener interface. * * @param - * e The TunnelConnectEvent describing the tunnel that was just connected + * event The TunnelConnectEvent describing the tunnel that was just connected * @return * false if the delegate listener rejects the tunnel connection, @@ -106,10 +105,10 @@ class ListenerFacade implements ListenerProvider { * if the delegate listener throws this exception */ @Override - public boolean tunnelConnected(TunnelConnectEvent e) + public boolean tunnelConnected(TunnelConnectEvent event) throws GuacamoleException { return !(delegate instanceof TunnelConnectListener) - || ((TunnelConnectListener) delegate).tunnelConnected(e); + || ((TunnelConnectListener) delegate).tunnelConnected(event); } /** @@ -117,7 +116,7 @@ class ListenerFacade implements ListenerProvider { * listener implements the TunnelCloseListener interface. * * @param - * e The TunnelCloseEvent describing the tunnel that is to be close + * event The TunnelCloseEvent describing the tunnel that is to be close * @return * false if the delegate listener rejects the tunnel close request, @@ -127,9 +126,9 @@ class ListenerFacade implements ListenerProvider { * if the delegate listener throws this exception */ @Override - public boolean tunnelClosed(TunnelCloseEvent e) throws GuacamoleException { + public boolean tunnelClosed(TunnelCloseEvent event) throws GuacamoleException { return !(delegate instanceof TunnelCloseListener) - || ((TunnelCloseListener) delegate).tunnelClosed(e); + || ((TunnelCloseListener) delegate).tunnelClosed(event); } } diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java index 0b3a7473d..1d8afc10c 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java @@ -15,7 +15,6 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * */ package org.apache.guacamole.extension; diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java b/guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java index 0620b4da8..376b213d6 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java @@ -15,7 +15,6 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * */ package org.apache.guacamole.extension; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java index b6bf5b20e..4fed42aeb 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java @@ -233,6 +233,7 @@ public class AuthenticationService { private void notifyAuthenticationSuccessListeners( AuthenticatedUser authenticatedUser, GuacamoleSession session) throws GuacamoleException { + UserContext userContext = null; if (session != null) { userContext = session.getUserContext( @@ -258,6 +259,7 @@ public class AuthenticationService { */ private void notifyAuthenticationFailureListeners(Credentials credentials) throws GuacamoleException { + listenerService.authenticationFailed(new AuthenticationFailureEvent(credentials)); } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java b/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java index 3b1a00f56..30c4357df 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java @@ -15,30 +15,26 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * */ package org.apache.guacamole.rest.event; +import java.util.List; import com.google.inject.Inject; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.extension.ListenerProvider; import org.apache.guacamole.net.event.AuthenticationFailureEvent; import org.apache.guacamole.net.event.AuthenticationSuccessEvent; -import org.apache.guacamole.net.event.TunnelCloseEvent; import org.apache.guacamole.net.event.TunnelConnectEvent; +import org.apache.guacamole.net.event.TunnelCloseEvent; import org.apache.guacamole.net.event.listener.AuthenticationFailureListener; import org.apache.guacamole.net.event.listener.AuthenticationSuccessListener; import org.apache.guacamole.net.event.listener.TunnelCloseListener; import org.apache.guacamole.net.event.listener.TunnelConnectListener; -import java.util.List; - /** * A service used to notify listeners registered by extensions when events of * interest occur. - * - * @author Carl Harris */ public class ListenerService implements ListenerProvider { @@ -50,7 +46,8 @@ public class ListenerService implements ListenerProvider { * are allowed to veto a successful authentication by returning false from the * listener method. Regardless of whether a particular listener rejects the * successful authentication, all listeners are notified. - * @param e + * + * @param event * The AuthenticationSuccessEvent describing the successful authentication * that just occurred. * @@ -62,11 +59,11 @@ public class ListenerService implements ListenerProvider { * some listeners may not receive the authentication success event notification. */ @Override - public boolean authenticationSucceeded(AuthenticationSuccessEvent e) + public boolean authenticationSucceeded(AuthenticationSuccessEvent event) throws GuacamoleException { boolean result = true; for (AuthenticationSuccessListener listener : listeners) { - result = result && listener.authenticationSucceeded(e); + result = result && listener.authenticationSucceeded(event); } return result; } @@ -74,7 +71,7 @@ public class ListenerService implements ListenerProvider { /** * Notifies all bound listeners of an authentication failure event. * - * @param e + * @param event * The AuthenticationSuccessEvent describing the authentication failure * that just occurred. * @@ -83,10 +80,10 @@ public class ListenerService implements ListenerProvider { * some listeners may not receive the authentication failure event notification. */ @Override - public void authenticationFailed(AuthenticationFailureEvent e) + public void authenticationFailed(AuthenticationFailureEvent event) throws GuacamoleException { for (AuthenticationFailureListener listener : listeners) { - listener.authenticationFailed(e); + listener.authenticationFailed(event); } } @@ -95,7 +92,7 @@ public class ListenerService implements ListenerProvider { * are allowed to veto a tunnel connection by returning false from the * listener method. Regardless of whether a particular listener rejects the * tunnel connection, all listeners are notified. - * @param e + * @param event * The TunnelConnectedEvent describing the tunnel that was just connected * * @return @@ -106,11 +103,11 @@ public class ListenerService implements ListenerProvider { * some listeners may not receive the tunnel connected event notification. */ @Override - public boolean tunnelConnected(TunnelConnectEvent e) + public boolean tunnelConnected(TunnelConnectEvent event) throws GuacamoleException { boolean result = true; for (TunnelConnectListener listener : listeners) { - result = result && listener.tunnelConnected(e); + result = result && listener.tunnelConnected(event); } return result; } @@ -120,7 +117,7 @@ public class ListenerService implements ListenerProvider { * are allowed to veto the request to close a tunnel by returning false from * the listener method. Regardless of whether a particular listener rejects the * tunnel close request, all listeners are notified. - * @param e + * @param event * The TunnelCloseEvent describing the tunnel that is to be closed * * @return @@ -131,10 +128,10 @@ public class ListenerService implements ListenerProvider { * some listeners may not receive the tunnel close event notification. */ @Override - public boolean tunnelClosed(TunnelCloseEvent e) throws GuacamoleException { + public boolean tunnelClosed(TunnelCloseEvent event) throws GuacamoleException { boolean result = true; for (TunnelCloseListener listener : listeners) { - result = result && listener.tunnelClosed(e); + result = result && listener.tunnelClosed(event); } return result; } From 10bc443864dfda50e6277ab65d92e2dffbce8f8c Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Thu, 17 Aug 2017 05:24:10 -0400 Subject: [PATCH 09/16] GUACAMOLE-364: eliminate unnecessary local variable --- .../org/apache/guacamole/rest/auth/AuthenticationService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java index 4fed42aeb..8760b7363 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java @@ -243,8 +243,7 @@ public class AuthenticationService { AuthenticationSuccessEvent event = new AuthenticationSuccessEvent( userContext, authenticatedUser.getCredentials()); - boolean ok = listenerService.authenticationSucceeded(event); - if (!ok) { + if (!listenerService.authenticationSucceeded(event)) { throw new GuacamoleAuthenticationRejectedException(); } } From beac46bff5e3070d931e5f5577a095f8edcdb070 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Thu, 17 Aug 2017 05:25:51 -0400 Subject: [PATCH 10/16] GUACAMOLE-364: revert unnecessary code simplification This change isn't necessary to the stated issue. The expression here is unnecessarily complex but that should be addressed in another issue. --- .../org/apache/guacamole/rest/auth/AuthenticationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java index 8760b7363..cca284524 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java @@ -295,7 +295,7 @@ public class AuthenticationService { } // Otherwise, attempt authentication as a new user - AuthenticatedUser authenticatedUser = authenticateUser(credentials); + AuthenticatedUser authenticatedUser = AuthenticationService.this.authenticateUser(credentials); notifyAuthenticationSuccessListeners(authenticatedUser, null); if (logger.isInfoEnabled()) From 2062f1ccc54fc6435b9d10d4607c56a1fa4ac164 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Thu, 17 Aug 2017 05:28:47 -0400 Subject: [PATCH 11/16] GUACAMOLE-364: eliminate unnecessary local variable --- .../java/org/apache/guacamole/tunnel/TunnelRequestService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java index 823ac1b9f..56cf82a59 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java @@ -91,8 +91,7 @@ public class TunnelRequestService { private void notifyTunnelConnectListeners(UserContext userContext, Credentials credentials, GuacamoleTunnel tunnel) throws GuacamoleException { TunnelConnectEvent event = new TunnelConnectEvent(userContext, credentials, tunnel); - boolean ok = listenerService.tunnelConnected(event); - if (!ok) { + if (!listenerService.tunnelConnected(event)) { try { tunnel.close(); } From f63c8b43a8277f9d38ada78f4acac72e02fcbed6 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Thu, 7 Sep 2017 16:58:44 -0400 Subject: [PATCH 12/16] GUACAMOLE-364: declare and implement new listener API This commit also deprecates the existing listener API and includes support for adapting existing listener implementations to the new API. --- ...camoleAuthenticationRejectedException.java | 33 --- .../GuacamoleTunnelConnectedException.java | 32 --- .../GuacamoleTunnelRejectedException.java | 32 --- .../net/event/AuthenticationSuccessEvent.java | 5 + .../guacamole/net/event/TunnelCloseEvent.java | 4 + .../net/event/TunnelConnectEvent.java | 4 + .../AuthenticationFailureListener.java | 6 +- .../AuthenticationSuccessListener.java | 6 +- .../net/event/listener/Listener.java | 29 +- .../event/listener/TunnelCloseListener.java | 6 +- .../event/listener/TunnelConnectListener.java | 6 +- .../apache/guacamole/extension/Extension.java | 10 +- .../guacamole/extension/ExtensionModule.java | 42 +-- .../guacamole/extension/ListenerFacade.java | 134 --------- .../guacamole/extension/ListenerFactory.java | 256 ++++++++++++++++++ .../rest/auth/AuthenticationService.java | 35 +-- .../guacamole/rest/event/ListenerService.java | 108 +------- .../tunnel/TunnelRequestService.java | 41 +-- 18 files changed, 378 insertions(+), 411 deletions(-) delete mode 100644 guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java delete mode 100644 guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java delete mode 100644 guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java delete mode 100644 guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java create mode 100644 guacamole/src/main/java/org/apache/guacamole/extension/ListenerFactory.java diff --git a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java deleted file mode 100644 index 907e0c947..000000000 --- a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleAuthenticationRejectedException.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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; - -/** - * An exception thrown when a successful authentication is rejected by a - * AuthenticationSuccessListener in an extension. - */ -public class GuacamoleAuthenticationRejectedException - extends GuacamoleSecurityException { - - public GuacamoleAuthenticationRejectedException() { - super("authentication rejected by listener extension"); - } - -} diff --git a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java deleted file mode 100644 index c7d21cf1d..000000000 --- a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelConnectedException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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; - -/** - * An exception thrown when a request to close a tunnel is vetoed by a - * TunnelCloseListener in an extension. - */ -public class GuacamoleTunnelConnectedException extends GuacamoleClientException { - - public GuacamoleTunnelConnectedException() { - super("tunnel close vetoed by listener extension"); - } - -} diff --git a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java deleted file mode 100644 index b9367c180..000000000 --- a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleTunnelRejectedException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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; - -/** - * An exception thrown when a successful tunnel connection is rejected by a - * TunnelConnectListener in an extension. - */ -public class GuacamoleTunnelRejectedException extends GuacamoleClientException { - - public GuacamoleTunnelRejectedException() { - super("tunnel connection rejected by listener extension"); - } - -} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationSuccessEvent.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationSuccessEvent.java index 2e5ae3a17..c72d669fa 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationSuccessEvent.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationSuccessEvent.java @@ -26,6 +26,11 @@ import org.apache.guacamole.net.auth.UserContext; * An event which is triggered whenever a user's credentials pass * authentication. The credentials that passed authentication are included * within this event, and can be retrieved using getCredentials(). + *

+ * If a {@link org.apache.guacamole.net.event.listener.Listener} throws + * a GuacamoleException when handling an event of this type, successful authentication + * is effectively vetoed and will be subsequently processed as though the + * authentication failed. */ public class AuthenticationSuccessEvent implements UserEvent, CredentialEvent { diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/TunnelCloseEvent.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/TunnelCloseEvent.java index ab453e87e..c0e2a622e 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/TunnelCloseEvent.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/TunnelCloseEvent.java @@ -28,6 +28,10 @@ import org.apache.guacamole.net.auth.UserContext; * being closed can be accessed through getTunnel(), and the UserContext * associated with the request which is closing the tunnel can be retrieved * with getUserContext(). + *

+ * If a {@link org.apache.guacamole.net.event.listener.Listener} throws + * a GuacamoleException when handling an event of this type, the request to close + * the tunnel is effectively vetoed and will remain connected. */ public class TunnelCloseEvent implements UserEvent, CredentialEvent, TunnelEvent { diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/TunnelConnectEvent.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/TunnelConnectEvent.java index acf5e8922..62828db8f 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/TunnelConnectEvent.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/TunnelConnectEvent.java @@ -28,6 +28,10 @@ import org.apache.guacamole.net.auth.UserContext; * being connected can be accessed through getTunnel(), and the UserContext * associated with the request which is connecting the tunnel can be retrieved * with getUserContext(). + *

+ * If a {@link org.apache.guacamole.net.event.listener.Listener} throws + * a GuacamoleException when handling an event of this type, the tunnel connection + * is effectively vetoed and will be subsequently closed. */ public class TunnelConnectEvent implements UserEvent, CredentialEvent, TunnelEvent { diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java index 86122bad2..1971af89b 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java @@ -26,8 +26,12 @@ import org.apache.guacamole.net.event.AuthenticationFailureEvent; * A listener whose authenticationFailed() hook will fire immediately * after a user's authentication attempt fails. Note that this hook cannot * be used to cancel the authentication failure. + * + * @deprecated + * Listeners should instead implement the {@link Listener} interface */ -public interface AuthenticationFailureListener extends Listener { +@Deprecated +public interface AuthenticationFailureListener { /** * Event hook which fires immediately after a user's authentication attempt diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java index cc5e01d57..77a6ed1b2 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java @@ -27,8 +27,12 @@ import org.apache.guacamole.net.event.AuthenticationSuccessEvent; * authentication attempt succeeds. If a user successfully authenticates, * the authenticationSucceeded() hook has the opportunity to cancel the * authentication and force it to fail. + * + * @deprecated + * Listeners should instead implement the {@link Listener} interface */ -public interface AuthenticationSuccessListener extends Listener { +@Deprecated +public interface AuthenticationSuccessListener { /** * Event hook which fires immediately after a user's authentication attempt diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java index 58499934e..d21f68681 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java @@ -19,10 +19,33 @@ package org.apache.guacamole.net.event.listener; +import org.apache.guacamole.GuacamoleException; + /** - * A marker interface extended by all listener types. This interface is used - * simply to validate that a listener class identified in an extension - * actually implements some listener interface. + * A listener for events that occur in handing various Guacamole requests + * such as authentication, tunnel connect/close, etc. Listeners are registered + * through the extension manifest mechanism. When an event occurs, listeners + * are notified in the order in which they are declared in the manifest and + * continues until either all listeners have been notified or with the first + * listener that throws a GuacamoleException or other runtime exception. */ public interface Listener { + + /** + * Notifies the recipient that an event has occurred. + *

+ * Throwing an exception from an event listener can act to veto an action in + * progress for some event types. See the Javadoc for specific event types for + * details. + * + * @param event + * an object that describes the subject event + * + * @throws GuacamoleException + * If the listener wishes to stop notification of the event to subsequent + * listeners. For some event types, this acts to veto an action in progress; + * e.g. treating a successful authentication as though it failed + */ + void handleEvent(Object event) throws GuacamoleException; + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java index 99f2c04de..70677b3ac 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java @@ -25,8 +25,12 @@ import org.apache.guacamole.net.event.TunnelCloseEvent; /** * A listener whose tunnelClosed() hook will fire immediately after an * existing tunnel is closed. + * + * @deprecated + * Listeners should instead implement the {@link Listener} interface */ -public interface TunnelCloseListener extends Listener { +@Deprecated +public interface TunnelCloseListener { /** * Event hook which fires immediately before an existing tunnel is closed. diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java index 7ac47e189..edc144e29 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java @@ -25,8 +25,12 @@ import org.apache.guacamole.net.event.TunnelConnectEvent; /** * A listener whose tunnelConnected() hook will fire immediately after a new * tunnel is connected. + * + * @deprecated + * Listeners should instead implement the {@link Listener} interface */ -public interface TunnelConnectListener extends Listener { +@Deprecated +public interface TunnelConnectListener { /** * Event hook which fires immediately after a new tunnel is connected. diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java b/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java index 697002d42..dc43b8f00 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java @@ -113,7 +113,7 @@ public class Extension { /** * The collection of all Listener classes defined within the extension. */ - private final Collection> listenerClasses; + private final Collection> listenerClasses; /** * The resource for the small favicon for the extension. If provided, this @@ -328,15 +328,15 @@ public class Extension { * If any given class does not exist, or if any given class is not a * subclass of AuthenticationProvider. */ - private Collection> getListenerClasses(Collection names) + private Collection> getListenerClasses(Collection names) throws GuacamoleException { // If no classnames are provided, just return an empty list if (names == null) - return Collections.>emptyList(); + return Collections.>emptyList(); // Define all auth provider classes - Collection> classes = new ArrayList>(names.size()); + Collection> classes = new ArrayList>(names.size()); for (String name : names) classes.add(getListenerClass(name)); @@ -578,7 +578,7 @@ public class Extension { * @return * All declared listener classes with this extension. */ - public Collection> getListenerClasses() { + public Collection> getListenerClasses() { return listenerClasses; } diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java index 1e1a85452..f3ca70067 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java @@ -95,8 +95,8 @@ public class ExtensionModule extends ServletModule { /** * All currently-bound authentication providers, if any. */ - private final List boundListenerProviders = - new ArrayList(); + private final List boundListners = + new ArrayList(); /** * Service for adding and retrieving language resources. @@ -195,19 +195,19 @@ public class ExtensionModule extends ServletModule { } /** - * Binds the given Listener class such that any service - * requiring access to the Listener can obtain it via - * injection, along with any other bound Listener. - * - * @param listenerClass - * The Listener class to bind. + * Binds the given provider class such that a listener is bound for each + * listener interface implemented by the provider and such that all bound + * listener instances can be obtained via injection. + * * + * @param providerClass + * The listener provider class to bind */ - private void bindListenerProvider(Class listenerClass) { + private void bindListeners(Class providerClass) { + + logger.debug("[{}] Binding listeners \"{}\".", + boundListners.size(), providerClass.getName()); + boundListners.addAll(ListenerFactory.createListeners(providerClass)); - // Bind listener - logger.debug("[{}] Binding Listener \"{}\".", - boundListenerProviders.size(), listenerClass.getName()); - boundListenerProviders.add(new ListenerFacade(listenerClass)); } /** @@ -218,23 +218,23 @@ public class ExtensionModule extends ServletModule { * @param listeners * The Listener classes to bind. */ - private void bindListenerProviders(Collection> listeners) { + private void bindListeners(Collection> listeners) { // Bind each listener within extension - for (Class listener : listeners) - bindListenerProvider(listener); + for (Class listener : listeners) + bindListeners(listener); } /** - * Returns a list of all currently-bound ListenerProvider instances. + * Returns a list of all currently-bound Listener instances. * * @return - * A List of all currently-bound ListenerProvider instances. The List is + * A List of all currently-bound Listener instances. The List is * not modifiable. */ @Provides - public List getListenerProviders() { - return Collections.unmodifiableList(boundListenerProviders); + public List getListeners() { + return Collections.unmodifiableList(boundListners); } /** @@ -378,7 +378,7 @@ public class ExtensionModule extends ServletModule { bindAuthenticationProviders(extension.getAuthenticationProviderClasses()); // Attempt to load all listeners - bindListenerProviders(extension.getListenerClasses()); + bindListeners(extension.getListenerClasses()); // Add any translation resources serveLanguageResources(extension.getTranslationResources()); diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java deleted file mode 100644 index ab0b7c85d..000000000 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFacade.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.extension; - -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.net.event.AuthenticationFailureEvent; -import org.apache.guacamole.net.event.AuthenticationSuccessEvent; -import org.apache.guacamole.net.event.TunnelCloseEvent; -import org.apache.guacamole.net.event.TunnelConnectEvent; -import org.apache.guacamole.net.event.listener.*; - -/** - * Provides a wrapper around a Listener subclass, allowing listener - * extensions to be bound without regard for which specific listener interfaces - * are implemented. - */ -class ListenerFacade implements ListenerProvider { - - private final Listener delegate; - - /** - * Creates a new ListenerFacade which delegates all listener methods - * calls to an instance of the given Listener subclass. If - * an instance of the given class cannot be created, creation of this - * facade will still succeed. Errors will be logged at the time listener - * creation fails, but subsequent events directed to the listener will be - * silently dropped. - * - * @param listenerClass - * The Listener subclass to instantiate. - */ - public ListenerFacade(Class listenerClass) { - delegate = ProviderFactory.newInstance("listener", listenerClass); - } - - /** - * Notifies the delegate listener of an authentication success event, if the - * listener implements the AuthenticationSuccessListener interface. - * - * @param - * event The AuthenticationSuccessEvent describing the authentication - * success that just occurred. - * @return - * false if the delegate listener rejects the successful authentication, - * else true - * - * @throws GuacamoleException - * if the delegate listener throws this exception - */ - @Override - public boolean authenticationSucceeded(AuthenticationSuccessEvent event) - throws GuacamoleException { - return !(delegate instanceof AuthenticationSuccessListener) - || ((AuthenticationSuccessListener) delegate).authenticationSucceeded(event); - } - - /** - * Notifies the delegate listener of an authentication failure event, if the - * listener implements the AuthenticationSuccessListener interface. - * - * @param - * event The AuthenticationFailureEvent describing the authentication - * failure that just occurred. - * - * @throws GuacamoleException - * if the delegate listener throws this exception - */ - @Override - public void authenticationFailed(AuthenticationFailureEvent event) - throws GuacamoleException { - if (delegate instanceof AuthenticationFailureListener) { - ((AuthenticationFailureListener) delegate).authenticationFailed(event); - } - } - - /** - * Notifies the delegate listener of a tunnel connected event, if the - * listener implements the TunnelConnectListener interface. - * - * @param - * event The TunnelConnectEvent describing the tunnel that was just connected - - * @return - * false if the delegate listener rejects the tunnel connection, - * else true - * - * @throws GuacamoleException - * if the delegate listener throws this exception - */ - @Override - public boolean tunnelConnected(TunnelConnectEvent event) - throws GuacamoleException { - return !(delegate instanceof TunnelConnectListener) - || ((TunnelConnectListener) delegate).tunnelConnected(event); - } - - /** - * Notifies the delegate listener of a tunnel close event, if the - * listener implements the TunnelCloseListener interface. - * - * @param - * event The TunnelCloseEvent describing the tunnel that is to be close - - * @return - * false if the delegate listener rejects the tunnel close request, - * else true - * - * @throws GuacamoleException - * if the delegate listener throws this exception - */ - @Override - public boolean tunnelClosed(TunnelCloseEvent event) throws GuacamoleException { - return !(delegate instanceof TunnelCloseListener) - || ((TunnelCloseListener) delegate).tunnelClosed(event); - } - -} diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFactory.java b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFactory.java new file mode 100644 index 000000000..c11f00f5d --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFactory.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.extension; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.net.event.AuthenticationFailureEvent; +import org.apache.guacamole.net.event.AuthenticationSuccessEvent; +import org.apache.guacamole.net.event.TunnelCloseEvent; +import org.apache.guacamole.net.event.TunnelConnectEvent; +import org.apache.guacamole.net.event.listener.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A factory that reflectively instantiates Listener objects for a given + * provider class. + */ +class ListenerFactory { + + /** + * Creates all listeners represented by an instance of the given provider class. + *

+ * If a provider class implements the simple Listener interface, that is the + * only listener type that will be returned. Otherwise, a list of Listener + * objects that adapt the legacy listener interfaces will be returned. + * + * @param providerClass + * a class that represents a listener provider + * + * @return + * list of listeners represented by the given provider class + */ + static List createListeners(Class providerClass) { + + Object provider = ProviderFactory.newInstance("listener", providerClass); + + if (provider instanceof Listener) { + return Collections.singletonList((Listener) provider); + } + + return createListenerAdapters(provider); + + } + + @SuppressWarnings("deprecation") + private static List createListenerAdapters(Object provider) { + + final List listeners = new ArrayList(); + + if (provider instanceof AuthenticationSuccessListener) { + listeners.add(new AuthenticationSuccessListenerAdapter( + (AuthenticationSuccessListener) provider)); + } + + if (provider instanceof AuthenticationFailureListener) { + listeners.add(new AuthenticationFailureListenerAdapter( + (AuthenticationFailureListener) provider)); + } + + if (provider instanceof TunnelConnectListener) { + listeners.add(new TunnelConnectListenerAdapter( + (TunnelConnectListener) provider)); + } + + if (provider instanceof TunnelCloseListener) { + listeners.add(new TunnelCloseListenerAdapter( + (TunnelCloseListener) provider)); + } + + return listeners; + + } + + /** + * An adapter the allows an AuthenticationSuccessListener to be used + * as an ordinary Listener. + */ + @SuppressWarnings("deprecation") + private static class AuthenticationSuccessListenerAdapter implements Listener { + + private final AuthenticationSuccessListener delegate; + + /** + * Constructs a new adapter. + * + * @param delegate + * the delegate listener + */ + AuthenticationSuccessListenerAdapter(AuthenticationSuccessListener delegate) { + this.delegate = delegate; + } + + /** + * Handles an AuthenticationSuccessEvent by passing the event to the delegate + * listener. If the delegate returns false, the adapter throws a GuacamoleException + * to veto the authentication success event. All other event types are ignored. + * + * @param event + * an object that describes the subject event + * + * @throws GuacamoleException + * if thrown by the delegate listener + */ + @Override + public void handleEvent(Object event) throws GuacamoleException { + if (event instanceof AuthenticationSuccessEvent) { + if (!delegate.authenticationSucceeded((AuthenticationSuccessEvent) event)) { + throw new GuacamoleSecurityException( + "listener vetoed successful authentication"); + } + } + } + + } + + /** + * An adapter the allows an AuthenticationFailureListener to be used + * as an ordinary Listener. + */ + @SuppressWarnings("deprecation") + private static class AuthenticationFailureListenerAdapter implements Listener { + + private final AuthenticationFailureListener delegate; + + /** + * Constructs a new adapter. + * + * @param delegate + * the delegate listener + */ + AuthenticationFailureListenerAdapter(AuthenticationFailureListener delegate) { + this.delegate = delegate; + } + + /** + * Handles an AuthenticationFailureEvent by passing the event to the delegate + * listener. All other event types are ignored. + * + * @param event + * an object that describes the subject event + * + * @throws GuacamoleException + * if thrown by the delegate listener + */ + @Override + public void handleEvent(Object event) throws GuacamoleException { + if (event instanceof AuthenticationFailureEvent) { + delegate.authenticationFailed((AuthenticationFailureEvent) event); + } + } + + } + + /** + * An adapter the allows a TunnelConnectListener to be used as an ordinary + * Listener. + */ + @SuppressWarnings("deprecation") + private static class TunnelConnectListenerAdapter implements Listener { + + private final TunnelConnectListener delegate; + + /** + * Constructs a new adapter. + * + * @param delegate + * the delegate listener + */ + TunnelConnectListenerAdapter(TunnelConnectListener delegate) { + this.delegate = delegate; + } + + /** + * Handles a TunnelConnectEvent by passing the event to the delegate listener. + * If the delegate returns false, the adapter throws a GuacamoleException + * to veto the tunnel connect event. All other event types are ignored. + * + * @param event + * an object that describes the subject event + * + * @throws GuacamoleException + * if thrown by the delegate listener + */ + @Override + public void handleEvent(Object event) throws GuacamoleException { + if (event instanceof TunnelConnectEvent) { + if (!delegate.tunnelConnected((TunnelConnectEvent) event)) { + throw new GuacamoleException("listener vetoed tunnel connection"); + } + } + } + + } + + /** + * An adapter the allows a TunnelCloseListener to be used as an ordinary + * Listener. + */ + @SuppressWarnings("deprecation") + private static class TunnelCloseListenerAdapter implements Listener { + + private final TunnelCloseListener delegate; + + /** + * Constructs a new adapter. + * + * @param delegate + * the delegate listener + */ + TunnelCloseListenerAdapter(TunnelCloseListener delegate) { + this.delegate = delegate; + } + + /** + * Handles a TunnelCloseEvent by passing the event to the delegate listener. + * If the delegate returns false, the adapter throws a GuacamoleException + * to veto the tunnel connect event. All other event types are ignored. + * + * @param event + * an object that describes the subject event + * + * @throws GuacamoleException + * if thrown by the delegate listener + */ + @Override + public void handleEvent(Object event) throws GuacamoleException { + if (event instanceof TunnelCloseEvent) { + if (!delegate.tunnelClosed((TunnelCloseEvent) event)) { + throw new GuacamoleException("listener vetoed tunnel close request"); + } + } + } + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java index cca284524..bdefc3efb 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java @@ -24,7 +24,7 @@ import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; -import org.apache.guacamole.GuacamoleAuthenticationRejectedException; + import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleUnauthorizedException; @@ -218,19 +218,17 @@ public class AuthenticationService { } /** - * Notify all bound AuthenticationSuccessListeners that a successful authentication - * has occurred. If any of the bound listeners returns false (indicating that the - * authentication should be rejected) a GuacamoleRejectedAuthenticationException is - * thrown. + * Notify all bound listeners that a successful authentication + * has occurred. * * @param authenticatedUser * The user that was successfully authenticated * @param session * Existing session for the user (if any) * @throws GuacamoleException - * If a filter throws an exception or if any filter rejects the authentication + * If thrown by a listener */ - private void notifyAuthenticationSuccessListeners( + private void fireAuthenticationSuccessEvent( AuthenticatedUser authenticatedUser, GuacamoleSession session) throws GuacamoleException { @@ -240,26 +238,21 @@ public class AuthenticationService { authenticatedUser.getAuthenticationProvider().getIdentifier()); } - AuthenticationSuccessEvent event = new AuthenticationSuccessEvent( - userContext, authenticatedUser.getCredentials()); - - if (!listenerService.authenticationSucceeded(event)) { - throw new GuacamoleAuthenticationRejectedException(); - } + listenerService.handleEvent(new AuthenticationSuccessEvent( + userContext, authenticatedUser.getCredentials())); } /** - * Notify all bound AuthenticationFailureListeners that an authentication has failed. + * Notify all bound listeners that an authentication attempt has failed. * * @param credentials * The credentials that failed to authenticate * @throws GuacamoleException - * If a filter throws an exception + * If thrown by a listener */ - private void notifyAuthenticationFailureListeners(Credentials credentials) + private void fireAuthenticationFailedEvent(Credentials credentials) throws GuacamoleException { - - listenerService.authenticationFailed(new AuthenticationFailureEvent(credentials)); + listenerService.handleEvent(new AuthenticationFailureEvent(credentials)); } /** @@ -290,13 +283,13 @@ public class AuthenticationService { if (existingSession != null) { AuthenticatedUser updatedUser = updateAuthenticatedUser( existingSession.getAuthenticatedUser(), credentials); - notifyAuthenticationSuccessListeners(updatedUser, existingSession); + fireAuthenticationSuccessEvent(updatedUser, existingSession); return updatedUser; } // Otherwise, attempt authentication as a new user AuthenticatedUser authenticatedUser = AuthenticationService.this.authenticateUser(credentials); - notifyAuthenticationSuccessListeners(authenticatedUser, null); + fireAuthenticationSuccessEvent(authenticatedUser, null); if (logger.isInfoEnabled()) logger.info("User \"{}\" successfully authenticated from {}.", @@ -310,7 +303,7 @@ public class AuthenticationService { // Log and rethrow any authentication errors catch (GuacamoleException e) { - notifyAuthenticationFailureListeners(credentials); + fireAuthenticationFailedEvent(credentials); // Get request and username for sake of logging HttpServletRequest request = credentials.getRequest(); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java b/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java index 30c4357df..bbff8ee4b 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java @@ -22,118 +22,32 @@ package org.apache.guacamole.rest.event; import java.util.List; import com.google.inject.Inject; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.extension.ListenerProvider; -import org.apache.guacamole.net.event.AuthenticationFailureEvent; -import org.apache.guacamole.net.event.AuthenticationSuccessEvent; -import org.apache.guacamole.net.event.TunnelConnectEvent; -import org.apache.guacamole.net.event.TunnelCloseEvent; -import org.apache.guacamole.net.event.listener.AuthenticationFailureListener; -import org.apache.guacamole.net.event.listener.AuthenticationSuccessListener; -import org.apache.guacamole.net.event.listener.TunnelCloseListener; -import org.apache.guacamole.net.event.listener.TunnelConnectListener; +import org.apache.guacamole.net.event.listener.Listener; /** * A service used to notify listeners registered by extensions when events of * interest occur. */ -public class ListenerService implements ListenerProvider { +public class ListenerService implements Listener { @Inject - private List listeners; + private List listeners; /** - * Notifies all bound listeners of an authentication success event. Listeners - * are allowed to veto a successful authentication by returning false from the - * listener method. Regardless of whether a particular listener rejects the - * successful authentication, all listeners are notified. + * Notifies registered listeners than an event has occurred. Notification continues + * until a given listener throws a GuacamoleException or other runtime exception, or + * until all listeners have been notified. * * @param event - * The AuthenticationSuccessEvent describing the successful authentication - * that just occurred. + * an object that describes the subject event * - * @return - * false if any bound listener returns false, else true - * - * @throws GuacamoleException - * If any bound listener throws this exception. If a listener throws an exception - * some listeners may not receive the authentication success event notification. + * @throws GuacamoleException if a registered listener throws a GuacamoleException */ @Override - public boolean authenticationSucceeded(AuthenticationSuccessEvent event) - throws GuacamoleException { - boolean result = true; - for (AuthenticationSuccessListener listener : listeners) { - result = result && listener.authenticationSucceeded(event); + public void handleEvent(Object event) throws GuacamoleException { + for (final Listener listener : listeners) { + listener.handleEvent(event); } - return result; - } - - /** - * Notifies all bound listeners of an authentication failure event. - * - * @param event - * The AuthenticationSuccessEvent describing the authentication failure - * that just occurred. - * - * @throws GuacamoleException - * If any bound listener throws this exception. If a listener throws an exception - * some listeners may not receive the authentication failure event notification. - */ - @Override - public void authenticationFailed(AuthenticationFailureEvent event) - throws GuacamoleException { - for (AuthenticationFailureListener listener : listeners) { - listener.authenticationFailed(event); - } - } - - /** - * Notifies all bound listeners of an tunnel connected event. Listeners - * are allowed to veto a tunnel connection by returning false from the - * listener method. Regardless of whether a particular listener rejects the - * tunnel connection, all listeners are notified. - * @param event - * The TunnelConnectedEvent describing the tunnel that was just connected - * - * @return - * false if any bound listener returns false, else true - * - * @throws GuacamoleException - * If any bound listener throws this exception. If a listener throws an exception - * some listeners may not receive the tunnel connected event notification. - */ - @Override - public boolean tunnelConnected(TunnelConnectEvent event) - throws GuacamoleException { - boolean result = true; - for (TunnelConnectListener listener : listeners) { - result = result && listener.tunnelConnected(event); - } - return result; - } - - /** - * Notifies all bound listeners of an tunnel close event. Listeners - * are allowed to veto the request to close a tunnel by returning false from - * the listener method. Regardless of whether a particular listener rejects the - * tunnel close request, all listeners are notified. - * @param event - * The TunnelCloseEvent describing the tunnel that is to be closed - * - * @return - * false if any bound listener returns false, else true - * - * @throws GuacamoleException - * If any bound listener throws this exception. If a listener throws an exception - * some listeners may not receive the tunnel close event notification. - */ - @Override - public boolean tunnelClosed(TunnelCloseEvent event) throws GuacamoleException { - boolean result = true; - for (TunnelCloseListener listener : listeners) { - result = result && listener.tunnelClosed(event); - } - return result; } } diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java index 56cf82a59..d3543d748 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java @@ -25,8 +25,6 @@ import java.util.List; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleSession; -import org.apache.guacamole.GuacamoleTunnelConnectedException; -import org.apache.guacamole.GuacamoleTunnelRejectedException; import org.apache.guacamole.GuacamoleUnauthorizedException; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.Connection; @@ -70,10 +68,8 @@ public class TunnelRequestService { private ListenerService listenerService; /** - * Notifies bound TunnelConnectListeners that a new tunnel has been connected. - * Listeners are allowed to veto a connected tunnel by returning false from the - * listener method. If the ListenerService indicates that any listener rejected - * the tunnel, the tunnel is closed an GuacamoleTunnelRejectedException is thrown. + * Notifies bound listeners that a new tunnel has been connected. + * Listeners may veto a connected tunnel by throwing any GuacamoleException. * * @param userContext * The UserContext associated with the user for whom the tunnel is @@ -88,25 +84,15 @@ public class TunnelRequestService { * @throws GuacamoleException * If thrown by a listener or if any listener vetoes the connected tunnel */ - private void notifyTunnelConnectListeners(UserContext userContext, + private void fireTunnelConnectEvent(UserContext userContext, Credentials credentials, GuacamoleTunnel tunnel) throws GuacamoleException { - TunnelConnectEvent event = new TunnelConnectEvent(userContext, credentials, tunnel); - if (!listenerService.tunnelConnected(event)) { - try { - tunnel.close(); - } - catch (GuacamoleException closeEx) { - logger.warn("Error closing rejected tunnel connection: {}", closeEx.getMessage()); - } - throw new GuacamoleTunnelRejectedException(); - } + listenerService.handleEvent(new TunnelConnectEvent(userContext, credentials, tunnel)); } /** - * Notifies bound TunnelCloseListeners that a tunnel is to be closed. - * Listeners are allowed to veto a request to close a tunnel by returning false from - * the listener method. If the ListenerService indicates that any listener vetoed the - * request to the close the tunnel, a GuacamoleTunnelConnectedException is thrown. + * Notifies bound listeners that a tunnel is to be closed. + * Listeners are allowed to veto a request to close a tunnel by throwing any + * GuacamoleException. * * @param userContext * The UserContext associated with the user for whom the tunnel is @@ -119,15 +105,12 @@ public class TunnelRequestService { * The tunnel that was connected * * @throws GuacamoleException - * If thrown by a listener or if any listener vetoes the request to close the tunnel + * If thrown by a listener. */ - private void notifyTunnelCloseListeners(UserContext userContext, + private void fireTunnelClosedEvent(UserContext userContext, Credentials credentials, GuacamoleTunnel tunnel) throws GuacamoleException { - TunnelCloseEvent event = new TunnelCloseEvent(userContext, credentials, tunnel); - if (listenerService.tunnelClosed(event)) { - throw new GuacamoleTunnelConnectedException(); - } + listenerService.handleEvent(new TunnelCloseEvent(userContext, credentials, tunnel)); } /** @@ -317,7 +300,7 @@ public class TunnelRequestService { public void close() throws GuacamoleException { // notify listeners to allow close request to be vetoed - notifyTunnelCloseListeners(context, + fireTunnelClosedEvent(context, session.getAuthenticatedUser().getCredentials(), tunnel); long connectionEndTime = System.currentTimeMillis(); @@ -406,7 +389,7 @@ public class TunnelRequestService { GuacamoleTunnel tunnel = createConnectedTunnel(userContext, type, id, info); // Notify listeners to allow connection to be vetoed - notifyTunnelConnectListeners(userContext, + fireTunnelConnectEvent(userContext, session.getAuthenticatedUser().getCredentials(), tunnel); // Associate tunnel with session From f05b108e9d507aa0936ca139b50b0b2ca1651406 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Mon, 25 Sep 2017 06:18:05 -0400 Subject: [PATCH 13/16] GUACAMOLE-364: delete unused interface --- .../guacamole/extension/ListenerProvider.java | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java deleted file mode 100644 index 1d8afc10c..000000000 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerProvider.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.extension; - -import org.apache.guacamole.net.event.listener.AuthenticationFailureListener; -import org.apache.guacamole.net.event.listener.AuthenticationSuccessListener; -import org.apache.guacamole.net.event.listener.TunnelCloseListener; -import org.apache.guacamole.net.event.listener.TunnelConnectListener; - -/** - * A provider of an event listener. While an implementation of this interface - * must implement all of the specified listener interfaces, an implementation - * may selectively deliver event notifications to an underlying delegate based - * on the specific listener interfaces implemented by the delegate. - */ -public interface ListenerProvider extends AuthenticationSuccessListener, - AuthenticationFailureListener, TunnelConnectListener, - TunnelCloseListener { -} From 2bdf49205cb260576959d6be0598dc58c4e6fbb8 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Mon, 25 Sep 2017 06:18:43 -0400 Subject: [PATCH 14/16] GUACAMOLE-364: fixed misspelled instance variable name --- .../org/apache/guacamole/extension/ExtensionModule.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java index f3ca70067..e7cdedb80 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java @@ -95,7 +95,7 @@ public class ExtensionModule extends ServletModule { /** * All currently-bound authentication providers, if any. */ - private final List boundListners = + private final List boundListeners = new ArrayList(); /** @@ -205,8 +205,8 @@ public class ExtensionModule extends ServletModule { private void bindListeners(Class providerClass) { logger.debug("[{}] Binding listeners \"{}\".", - boundListners.size(), providerClass.getName()); - boundListners.addAll(ListenerFactory.createListeners(providerClass)); + boundListeners.size(), providerClass.getName()); + boundListeners.addAll(ListenerFactory.createListeners(providerClass)); } @@ -234,7 +234,7 @@ public class ExtensionModule extends ServletModule { */ @Provides public List getListeners() { - return Collections.unmodifiableList(boundListners); + return Collections.unmodifiableList(boundListeners); } /** From f8484befaf44fb0e7dfad80a3a07ca96e0f2d959 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Fri, 6 Oct 2017 10:22:10 -0400 Subject: [PATCH 15/16] GUACAMOLE-364: addressed various documentation issues --- .../AuthenticationFailureListener.java | 17 +++--- .../AuthenticationSuccessListener.java | 21 ++++--- .../net/event/listener/Listener.java | 4 +- .../event/listener/TunnelCloseListener.java | 25 ++++---- .../event/listener/TunnelConnectListener.java | 37 ++++++------ .../extension/ExtensionManifest.java | 2 +- .../guacamole/extension/ExtensionModule.java | 10 ++-- .../guacamole/extension/ListenerFactory.java | 58 +++++++++++++------ .../guacamole/extension/ProviderFactory.java | 18 ++++-- .../rest/auth/AuthenticationService.java | 15 +++-- .../guacamole/rest/event/ListenerService.java | 8 ++- .../tunnel/TunnelRequestService.java | 10 ++-- 12 files changed, 136 insertions(+), 89 deletions(-) diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java index 1971af89b..6e707e6c5 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationFailureListener.java @@ -28,7 +28,7 @@ import org.apache.guacamole.net.event.AuthenticationFailureEvent; * be used to cancel the authentication failure. * * @deprecated - * Listeners should instead implement the {@link Listener} interface + * Listeners should instead implement the {@link Listener} interface. */ @Deprecated public interface AuthenticationFailureListener { @@ -37,12 +37,15 @@ public interface AuthenticationFailureListener { * Event hook which fires immediately after a user's authentication attempt * fails. * - * @param e The AuthenticationFailureEvent describing the authentication - * failure that just occurred. - * @throws GuacamoleException If an error occurs while handling the - * authentication failure event. Note that - * throwing an exception will NOT cause the - * authentication failure to be canceled. + * @param e + * The AuthenticationFailureEvent describing the authentication + * failure that just occurred. + * + * @throws GuacamoleException + * If an error occurs while handling the authentication failure event. + * Note that throwing an exception will NOT cause the authentication + * failure to be canceled (which makes no sense), but it will prevent + * subsequent listeners from receiving the notification. */ void authenticationFailed(AuthenticationFailureEvent e) throws GuacamoleException; diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java index 77a6ed1b2..29be27cf6 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java @@ -39,15 +39,18 @@ public interface AuthenticationSuccessListener { * succeeds. The return value of this hook dictates whether the * successful authentication attempt is canceled. * - * @param e The AuthenticationFailureEvent describing the authentication - * failure that just occurred. - * @return true if the successful authentication attempt should be - * allowed, or false if the attempt should be denied, causing - * the attempt to effectively fail. - * @throws GuacamoleException If an error occurs while handling the - * authentication success event. Throwing an - * exception will also cancel the authentication - * success. + * @param e + * The AuthenticationFailureEvent describing the authentication + * failure that just occurred. + * + * @return + * true if the successful authentication attempt should be + * allowed, or false if the attempt should be denied, causing + * the attempt to effectively fail. + * + * @throws GuacamoleException + * If an error occurs while handling the authentication success event. + * Throwing an exception will also cancel the authentication success. */ boolean authenticationSucceeded(AuthenticationSuccessEvent e) throws GuacamoleException; diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java index d21f68681..af480b7e0 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/Listener.java @@ -39,12 +39,12 @@ public interface Listener { * details. * * @param event - * an object that describes the subject event + * An object that describes the event that has occurred. * * @throws GuacamoleException * If the listener wishes to stop notification of the event to subsequent * listeners. For some event types, this acts to veto an action in progress; - * e.g. treating a successful authentication as though it failed + * e.g. treating a successful authentication as though it failed. */ void handleEvent(Object event) throws GuacamoleException; diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java index 70677b3ac..82da7250b 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java @@ -27,7 +27,7 @@ import org.apache.guacamole.net.event.TunnelCloseEvent; * existing tunnel is closed. * * @deprecated - * Listeners should instead implement the {@link Listener} interface + * Listeners should instead implement the {@link Listener} interface. */ @Deprecated public interface TunnelCloseListener { @@ -37,16 +37,19 @@ public interface TunnelCloseListener { * The return value of this hook dictates whether the tunnel is allowed to * be closed. * - * @param e The TunnelCloseEvent describing the tunnel being closed and - * any associated credentials. - * @return true if the tunnel should be allowed to be closed, or false - * if the attempt should be denied, causing the attempt to - * effectively fail. - * @throws GuacamoleException If an error occurs while handling the - * tunnel close event. Throwing an exception - * will also stop the tunnel from being closed. + * @param e + * The TunnelCloseEvent describing the tunnel being closed and + * any associated credentials. + * + * @return + * true if the tunnel should be allowed to be closed, or false + * if the attempt should be denied, causing the attempt to + * effectively fail. + * + * @throws GuacamoleException + * If an error occurs while handling the tunnel close event. Throwing + * an exception will also stop the tunnel from being closed. */ - boolean tunnelClosed(TunnelCloseEvent e) - throws GuacamoleException; + boolean tunnelClosed(TunnelCloseEvent e) throws GuacamoleException; } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java index edc144e29..e224f7430 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelConnectListener.java @@ -27,27 +27,30 @@ import org.apache.guacamole.net.event.TunnelConnectEvent; * tunnel is connected. * * @deprecated - * Listeners should instead implement the {@link Listener} interface + * Listeners should instead implement the {@link Listener} interface. */ @Deprecated public interface TunnelConnectListener { /** - * Event hook which fires immediately after a new tunnel is connected. - * The return value of this hook dictates whether the tunnel is made visible - * to the session. - * - * @param e The TunnelConnectEvent describing the tunnel being connected and - * any associated credentials. - * @return true if the tunnel should be allowed to be connected, or false - * if the attempt should be denied, causing the attempt to - * effectively fail. - * @throws GuacamoleException If an error occurs while handling the - * tunnel connect event. Throwing an exception - * will also stop the tunnel from being made - * visible to the session. - */ - boolean tunnelConnected(TunnelConnectEvent e) - throws GuacamoleException; + * Event hook which fires immediately after a new tunnel is connected. + * The return value of this hook dictates whether the tunnel is made visible + * to the session. + * + * @param e + * The TunnelConnectEvent describing the tunnel being connected and + * any associated credentials. + * + * @return + * true if the tunnel should be allowed to be connected, or false + * if the attempt should be denied, causing the attempt to + * effectively fail. + * + * @throws GuacamoleException + * If an error occurs while handling the tunnel connect event. Throwing + * an exception will also stop the tunnel from being made visible to the + * session. + */ + boolean tunnelConnected(TunnelConnectEvent e) throws GuacamoleException; } diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java index 1636b0350..054d9ef3d 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java @@ -380,7 +380,7 @@ public class ExtensionManifest { * class name. * * @param listeners - * a collection of classnames for all listeners within the extension + * A collection of classnames for all listeners within the extension. */ public void setListeners(Collection listeners) { this.listeners = listeners; diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java index e7cdedb80..a74c4c0d6 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java @@ -198,13 +198,13 @@ public class ExtensionModule extends ServletModule { * Binds the given provider class such that a listener is bound for each * listener interface implemented by the provider and such that all bound * listener instances can be obtained via injection. - * * + * * @param providerClass - * The listener provider class to bind + * The listener class to bind. */ - private void bindListeners(Class providerClass) { + private void bindListener(Class providerClass) { - logger.debug("[{}] Binding listeners \"{}\".", + logger.debug("[{}] Binding listener \"{}\".", boundListeners.size(), providerClass.getName()); boundListeners.addAll(ListenerFactory.createListeners(providerClass)); @@ -222,7 +222,7 @@ public class ExtensionModule extends ServletModule { // Bind each listener within extension for (Class listener : listeners) - bindListeners(listener); + bindListener(listener); } /** diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFactory.java b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFactory.java index c11f00f5d..8aa6babb4 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFactory.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ListenerFactory.java @@ -45,10 +45,10 @@ class ListenerFactory { * objects that adapt the legacy listener interfaces will be returned. * * @param providerClass - * a class that represents a listener provider + * A class that represents a listener. * * @return - * list of listeners represented by the given provider class + * The list of listeners represented by the given provider class. */ static List createListeners(Class providerClass) { @@ -62,6 +62,16 @@ class ListenerFactory { } + /** + * Creates a list of adapters for the given object, based on the legacy + * listener interfaces it implements. + * + * @param provider + * An object that implements zero or more legacy listener interfaces. + * + * @return + * The list of listeners represented by the given provider class. + */ @SuppressWarnings("deprecation") private static List createListenerAdapters(Object provider) { @@ -98,13 +108,16 @@ class ListenerFactory { @SuppressWarnings("deprecation") private static class AuthenticationSuccessListenerAdapter implements Listener { + /** + * The delegate listener for this adapter. + */ private final AuthenticationSuccessListener delegate; /** - * Constructs a new adapter. + * Constructs a new adapter that delivers events to the given delegate. * * @param delegate - * the delegate listener + * The delegate listener. */ AuthenticationSuccessListenerAdapter(AuthenticationSuccessListener delegate) { this.delegate = delegate; @@ -116,10 +129,10 @@ class ListenerFactory { * to veto the authentication success event. All other event types are ignored. * * @param event - * an object that describes the subject event + * An object that describes the event that occurred. * * @throws GuacamoleException - * if thrown by the delegate listener + * If thrown by the delegate listener. */ @Override public void handleEvent(Object event) throws GuacamoleException { @@ -140,13 +153,16 @@ class ListenerFactory { @SuppressWarnings("deprecation") private static class AuthenticationFailureListenerAdapter implements Listener { + /** + * The delegate listener for this adapter. + */ private final AuthenticationFailureListener delegate; /** - * Constructs a new adapter. + * Constructs a new adapter that delivers events to the given delegate. * * @param delegate - * the delegate listener + * The delegate listener. */ AuthenticationFailureListenerAdapter(AuthenticationFailureListener delegate) { this.delegate = delegate; @@ -157,10 +173,10 @@ class ListenerFactory { * listener. All other event types are ignored. * * @param event - * an object that describes the subject event + * An object that describes the event that occurred. * * @throws GuacamoleException - * if thrown by the delegate listener + * If thrown by the delegate listener. */ @Override public void handleEvent(Object event) throws GuacamoleException { @@ -178,13 +194,16 @@ class ListenerFactory { @SuppressWarnings("deprecation") private static class TunnelConnectListenerAdapter implements Listener { + /** + * The delegate listener for this adapter. + */ private final TunnelConnectListener delegate; /** - * Constructs a new adapter. + * Constructs a new adapter that delivers events to the given delegate. * * @param delegate - * the delegate listener + * The delegate listener. */ TunnelConnectListenerAdapter(TunnelConnectListener delegate) { this.delegate = delegate; @@ -196,10 +215,10 @@ class ListenerFactory { * to veto the tunnel connect event. All other event types are ignored. * * @param event - * an object that describes the subject event + * An object that describes the event that occurred. * * @throws GuacamoleException - * if thrown by the delegate listener + * If thrown by the delegate listener. */ @Override public void handleEvent(Object event) throws GuacamoleException { @@ -219,13 +238,16 @@ class ListenerFactory { @SuppressWarnings("deprecation") private static class TunnelCloseListenerAdapter implements Listener { + /** + * The delegate listener for this adapter. + */ private final TunnelCloseListener delegate; /** - * Constructs a new adapter. + * Constructs a new adapter that delivers events to the given delegate. * * @param delegate - * the delegate listener + * The delegate listener. */ TunnelCloseListenerAdapter(TunnelCloseListener delegate) { this.delegate = delegate; @@ -237,10 +259,10 @@ class ListenerFactory { * to veto the tunnel connect event. All other event types are ignored. * * @param event - * an object that describes the subject event + * An object that describes the event that occurred. * * @throws GuacamoleException - * if thrown by the delegate listener + * If thrown by the delegate listener. */ @Override public void handleEvent(Object event) throws GuacamoleException { diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java b/guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java index 376b213d6..01fda5719 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ProviderFactory.java @@ -26,24 +26,30 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; /** - * Static factory method for creating provider instances and logging unexpected outcomes - * with sufficient detail to allow user/developer debugging. + * A utility for creating provider instances and logging unexpected outcomes + * with sufficient detail to allow debugging. */ class ProviderFactory { + /** + * Logger used to log unexpected outcomes. + */ private static final Logger logger = LoggerFactory.getLogger(ProviderFactory.class); /** * Creates an instance of the specified provider class using the no-arg constructor. * * @param typeName - * The provider type name used for log messages (e.g. "authentication provider") + * The provider type name used for log messages; e.g. "authentication provider". + * * @param providerClass - * The provider class to instantiate + * The provider class to instantiate. + * * @param - * The provider type + * The provider type. + * * @return - * A provider instance or null if no instance was created due to error + * A provider instance or null if no instance was created due to error. */ static T newInstance(String typeName, Class providerClass) { T instance = null; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java index bdefc3efb..b18f00f4a 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java @@ -79,7 +79,7 @@ public class AuthenticationService { private AuthTokenGenerator authTokenGenerator; /** - * The service to use to notify registered authentication listeners + * The service to use to notify registered authentication listeners. */ @Inject private ListenerService listenerService; @@ -222,11 +222,13 @@ public class AuthenticationService { * has occurred. * * @param authenticatedUser - * The user that was successfully authenticated + * The user that was successfully authenticated. + * * @param session - * Existing session for the user (if any) + * The existing session for the user (if any). + * * @throws GuacamoleException - * If thrown by a listener + * If thrown by a listener. */ private void fireAuthenticationSuccessEvent( AuthenticatedUser authenticatedUser, GuacamoleSession session) @@ -246,9 +248,10 @@ public class AuthenticationService { * Notify all bound listeners that an authentication attempt has failed. * * @param credentials - * The credentials that failed to authenticate + * The credentials that failed to authenticate. + * * @throws GuacamoleException - * If thrown by a listener + * If thrown by a listener. */ private void fireAuthenticationFailedEvent(Credentials credentials) throws GuacamoleException { diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java b/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java index bbff8ee4b..e92cc8a66 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java @@ -30,6 +30,9 @@ import org.apache.guacamole.net.event.listener.Listener; */ public class ListenerService implements Listener { + /** + * The collection of registered listeners. + */ @Inject private List listeners; @@ -39,9 +42,10 @@ public class ListenerService implements Listener { * until all listeners have been notified. * * @param event - * an object that describes the subject event + * An object that describes the event that has occurred. * - * @throws GuacamoleException if a registered listener throws a GuacamoleException + * @throws GuacamoleException + * If a registered listener throws a GuacamoleException. */ @Override public void handleEvent(Object event) throws GuacamoleException { diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java index d3543d748..b029a3050 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java @@ -76,13 +76,13 @@ public class TunnelRequestService { * being created. * * @param credentials - * Credentials that authenticate the user + * Credentials that authenticate the user. * * @param tunnel - * The tunnel that was connected + * The tunnel that was connected. * * @throws GuacamoleException - * If thrown by a listener or if any listener vetoes the connected tunnel + * If thrown by a listener or if any listener vetoes the connected tunnel. */ private void fireTunnelConnectEvent(UserContext userContext, Credentials credentials, GuacamoleTunnel tunnel) throws GuacamoleException { @@ -99,10 +99,10 @@ public class TunnelRequestService { * being created. * * @param credentials - * Credentials that authenticate the user + * Credentials that authenticate the user. * * @param tunnel - * The tunnel that was connected + * The tunnel that was connected. * * @throws GuacamoleException * If thrown by a listener. From 25705a86e85f392deba3bcf43a8d669f70b8fce4 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Fri, 6 Oct 2017 10:26:26 -0400 Subject: [PATCH 16/16] GUACAMOLE-364: addressed some documentation stragglers --- .../net/event/listener/AuthenticationSuccessListener.java | 2 +- .../guacamole/net/event/listener/TunnelCloseListener.java | 2 +- .../java/org/apache/guacamole/extension/ExtensionManifest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java index 29be27cf6..6ba05a3f1 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/AuthenticationSuccessListener.java @@ -29,7 +29,7 @@ import org.apache.guacamole.net.event.AuthenticationSuccessEvent; * authentication and force it to fail. * * @deprecated - * Listeners should instead implement the {@link Listener} interface + * Listeners should instead implement the {@link Listener} interface. */ @Deprecated public interface AuthenticationSuccessListener { diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java index 82da7250b..84d765815 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/listener/TunnelCloseListener.java @@ -23,7 +23,7 @@ import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.event.TunnelCloseEvent; /** - * A listener whose tunnelClosed() hook will fire immediately after an + * A listener whose tunnelClosed() hook will fire immediately before an * existing tunnel is closed. * * @deprecated diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java index 054d9ef3d..2ed6c7579 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java @@ -367,7 +367,7 @@ public class ExtensionManifest { * class name. * * @return - * a collection of classnames for all listeners within the extension + * A collection of classnames for all listeners within the extension. */ public Collection getListeners() { return listeners;