1 package net.sourceforge.guacamole.net.basic;
4 * Guacamole - Clientless Remote Desktop
5 * Copyright (C) 2010 Michael Jumper
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 import java.io.IOException;
22 import java.util.Collection;
24 import javax.servlet.ServletException;
25 import javax.servlet.http.HttpServletRequest;
26 import javax.servlet.http.HttpServletResponse;
27 import javax.servlet.http.HttpSession;
28 import net.sourceforge.guacamole.GuacamoleException;
29 import net.sourceforge.guacamole.GuacamoleSecurityException;
30 import net.sourceforge.guacamole.net.InetGuacamoleSocket;
31 import net.sourceforge.guacamole.protocol.GuacamoleConfiguration;
32 import net.sourceforge.guacamole.properties.GuacamoleProperties;
33 import net.sourceforge.guacamole.net.GuacamoleSocket;
34 import net.sourceforge.guacamole.net.GuacamoleTunnel;
35 import net.sourceforge.guacamole.net.auth.Credentials;
36 import net.sourceforge.guacamole.net.basic.event.SessionListenerCollection;
37 import net.sourceforge.guacamole.net.event.TunnelCloseEvent;
38 import net.sourceforge.guacamole.net.event.TunnelConnectEvent;
39 import net.sourceforge.guacamole.net.event.listener.TunnelCloseListener;
40 import net.sourceforge.guacamole.net.event.listener.TunnelConnectListener;
41 import net.sourceforge.guacamole.protocol.ConfiguredGuacamoleSocket;
42 import net.sourceforge.guacamole.servlet.GuacamoleHTTPTunnelServlet;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * Connects users to a tunnel associated with the authorized configuration
48 * having the given ID.
50 * @author Michael Jumper
52 public class BasicGuacamoleTunnelServlet extends AuthenticatingHttpServlet {
54 private Logger logger = LoggerFactory.getLogger(BasicGuacamoleTunnelServlet.class);
57 protected void authenticatedService(
58 Map<String, GuacamoleConfiguration> configs,
59 HttpServletRequest request, HttpServletResponse response)
60 throws IOException, ServletException {
62 // If authenticated, respond as tunnel
63 tunnelServlet.service(request, response);
68 * Notifies all listeners in the given collection that a tunnel has been
71 * @param listeners A collection of all listeners that should be notified.
72 * @param credentials The credentials associated with the authentication
73 * request that connected the tunnel.
74 * @return true if all listeners are allowing the tunnel to connect,
75 * or if there are no listeners, and false if any listener is
76 * canceling the connection. Note that once one listener cancels,
77 * no other listeners will run.
78 * @throws GuacamoleException If any listener throws an error while being
79 * notified. Note that if any listener throws an
80 * error, the connect is canceled, and no other
83 private boolean notifyConnect(Collection listeners,
84 Credentials credentials, GuacamoleTunnel tunnel)
85 throws GuacamoleException {
87 // Build event for auth success
88 TunnelConnectEvent event = new TunnelConnectEvent(credentials, tunnel);
90 // Notify all listeners
91 for (Object listener : listeners) {
92 if (listener instanceof TunnelConnectListener) {
94 // Cancel immediately if hook returns false
95 if (!((TunnelConnectListener) listener).tunnelConnected(event))
106 * Notifies all listeners in the given collection that a tunnel has been
109 * @param listeners A collection of all listeners that should be notified.
110 * @param credentials The credentials associated with the authentication
111 * request that closed the tunnel.
112 * @return true if all listeners are allowing the tunnel to close,
113 * or if there are no listeners, and false if any listener is
114 * canceling the close. Note that once one listener cancels,
115 * no other listeners will run.
116 * @throws GuacamoleException If any listener throws an error while being
117 * notified. Note that if any listener throws an
118 * error, the close is canceled, and no other
119 * listeners will run.
121 private boolean notifyClose(Collection listeners,
122 Credentials credentials, GuacamoleTunnel tunnel)
123 throws GuacamoleException {
125 // Build event for auth success
126 TunnelCloseEvent event = new TunnelCloseEvent(credentials, tunnel);
128 // Notify all listeners
129 for (Object listener : listeners) {
130 if (listener instanceof TunnelCloseListener) {
132 // Cancel immediately if hook returns false
133 if (!((TunnelCloseListener) listener).tunnelClosed(event))
144 * Wrapped GuacamoleHTTPTunnelServlet which will handle all authenticated
147 private GuacamoleHTTPTunnelServlet tunnelServlet = new GuacamoleHTTPTunnelServlet() {
150 protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException {
152 HttpSession httpSession = request.getSession(true);
155 final SessionListenerCollection listeners;
157 listeners = new SessionListenerCollection(httpSession);
159 catch (GuacamoleException e) {
160 logger.error("Failed to retrieve listeners. Authentication canceled.", e);
164 // Get ID of connection
165 String id = request.getParameter("id");
168 final Credentials credentials = getCredentials(httpSession);
170 // Get authorized configs
171 Map<String, GuacamoleConfiguration> configs = getConfigurations(httpSession);
173 // If no configs/credentials in session, not authorized
174 if (credentials == null || configs == null)
175 throw new GuacamoleException("Cannot connect - user not logged in.");
177 // Get authorized config
178 GuacamoleConfiguration config = configs.get(id);
179 if (config == null) {
180 logger.error("Configuration id={} not found.", id);
181 throw new GuacamoleSecurityException("Requested configuration is not authorized.");
184 logger.info("Successful connection from {} to \"{}\".", request.getRemoteAddr(), id);
186 // Configure and connect socket
187 String hostname = GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_HOSTNAME);
188 int port = GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_PORT);
190 GuacamoleSocket socket = new ConfiguredGuacamoleSocket(
191 new InetGuacamoleSocket(hostname, port),
195 // Associate socket with tunnel
196 GuacamoleTunnel tunnel = new GuacamoleTunnel(socket) {
199 public void close() throws GuacamoleException {
201 // Only close if not canceled
202 if (!notifyClose(listeners, credentials, this))
203 throw new GuacamoleException("Tunnel close canceled by listener.");
205 // Close if no exception due to listener
212 // Notify listeners about connection
213 if (!notifyConnect(listeners, credentials, tunnel)) {
214 logger.info("Connection canceled by listener.");