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.net.InetGuacamoleSocket;
30 import net.sourceforge.guacamole.protocol.GuacamoleConfiguration;
31 import net.sourceforge.guacamole.properties.GuacamoleProperties;
32 import net.sourceforge.guacamole.net.GuacamoleSocket;
33 import net.sourceforge.guacamole.net.GuacamoleTunnel;
34 import net.sourceforge.guacamole.net.auth.Credentials;
35 import net.sourceforge.guacamole.net.basic.event.SessionListenerCollection;
36 import net.sourceforge.guacamole.net.event.TunnelCloseEvent;
37 import net.sourceforge.guacamole.net.event.TunnelConnectEvent;
38 import net.sourceforge.guacamole.net.event.listener.TunnelCloseListener;
39 import net.sourceforge.guacamole.net.event.listener.TunnelConnectListener;
40 import net.sourceforge.guacamole.protocol.ConfiguredGuacamoleSocket;
41 import net.sourceforge.guacamole.servlet.GuacamoleHTTPTunnelServlet;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
46 * Connects users to a tunnel associated with the authorized configuration
47 * having the given ID.
49 * @author Michael Jumper
51 public class BasicGuacamoleTunnelServlet extends AuthenticatingHttpServlet {
53 private Logger logger = LoggerFactory.getLogger(BasicGuacamoleTunnelServlet.class);
56 protected void authenticatedService(
57 Map<String, GuacamoleConfiguration> configs,
58 HttpServletRequest request, HttpServletResponse response)
59 throws IOException, ServletException {
61 // If authenticated, respond as tunnel
62 tunnelServlet.service(request, response);
67 * Notifies all listeners in the given collection that a tunnel has been
70 * @param listeners A collection of all listeners that should be notified.
71 * @param credentials The credentials associated with the authentication
72 * request that connected the tunnel.
73 * @return true if all listeners are allowing the tunnel to connect,
74 * or if there are no listeners, and false if any listener is
75 * canceling the connection. Note that once one listener cancels,
76 * no other listeners will run.
77 * @throws GuacamoleException If any listener throws an error while being
78 * notified. Note that if any listener throws an
79 * error, the connect is canceled, and no other
82 private boolean notifyConnect(Collection listeners,
83 Credentials credentials, GuacamoleTunnel tunnel)
84 throws GuacamoleException {
86 // Build event for auth success
87 TunnelConnectEvent event = new TunnelConnectEvent(credentials, tunnel);
89 // Notify all listeners
90 for (Object listener : listeners) {
91 if (listener instanceof TunnelConnectListener) {
93 // Cancel immediately if hook returns false
94 if (!((TunnelConnectListener) listener).tunnelConnected(event))
105 * Notifies all listeners in the given collection that a tunnel has been
108 * @param listeners A collection of all listeners that should be notified.
109 * @param credentials The credentials associated with the authentication
110 * request that closed the tunnel.
111 * @return true if all listeners are allowing the tunnel to close,
112 * or if there are no listeners, and false if any listener is
113 * canceling the close. Note that once one listener cancels,
114 * no other listeners will run.
115 * @throws GuacamoleException If any listener throws an error while being
116 * notified. Note that if any listener throws an
117 * error, the close is canceled, and no other
118 * listeners will run.
120 private boolean notifyClose(Collection listeners,
121 Credentials credentials, GuacamoleTunnel tunnel)
122 throws GuacamoleException {
124 // Build event for auth success
125 TunnelCloseEvent event = new TunnelCloseEvent(credentials, tunnel);
127 // Notify all listeners
128 for (Object listener : listeners) {
129 if (listener instanceof TunnelCloseListener) {
131 // Cancel immediately if hook returns false
132 if (!((TunnelCloseListener) listener).tunnelClosed(event))
143 * Wrapped GuacamoleHTTPTunnelServlet which will handle all authenticated
146 private GuacamoleHTTPTunnelServlet tunnelServlet = new GuacamoleHTTPTunnelServlet() {
149 protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException {
151 HttpSession httpSession = request.getSession(true);
154 final SessionListenerCollection listeners;
156 listeners = new SessionListenerCollection(httpSession);
158 catch (GuacamoleException e) {
159 logger.error("Failed to retrieve listeners. Authentication canceled.", e);
163 // Get ID of connection
164 String id = request.getParameter("id");
167 final Credentials credentials = getCredentials(httpSession);
169 // Get authorized configs
170 Map<String, GuacamoleConfiguration> configs = getConfigurations(httpSession);
172 // If no configs/credentials in session, not authorized
173 if (credentials == null || configs == null)
174 throw new GuacamoleException("Cannot connect - user not logged in.");
176 // Get authorized config
177 GuacamoleConfiguration config = configs.get(id);
178 if (config == null) {
179 logger.error("Configuration id={} not found.", id);
183 logger.info("Successful connection from {} to \"{}\".", request.getRemoteAddr(), id);
185 // Configure and connect socket
186 String hostname = GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_HOSTNAME);
187 int port = GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_PORT);
189 GuacamoleSocket socket = new ConfiguredGuacamoleSocket(
190 new InetGuacamoleSocket(hostname, port),
194 // Associate socket with tunnel
195 GuacamoleTunnel tunnel = new GuacamoleTunnel(socket) {
198 public void close() throws GuacamoleException {
200 // Only close if not canceled
201 if (!notifyClose(listeners, credentials, this))
202 throw new GuacamoleException("Tunnel close canceled by listener.");
204 // Close if no exception due to listener
211 // Notify listeners about connection
212 if (!notifyConnect(listeners, credentials, tunnel)) {
213 logger.info("Connection canceled by listener.");