2 package net.sourceforge.guacamole.net.basic;
4 import java.io.IOException;
5 import java.util.Collection;
7 import javax.servlet.ServletException;
8 import javax.servlet.http.HttpServlet;
9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11 import javax.servlet.http.HttpSession;
12 import net.sourceforge.guacamole.GuacamoleException;
13 import net.sourceforge.guacamole.net.auth.AuthenticationProvider;
14 import net.sourceforge.guacamole.net.auth.Credentials;
15 import net.sourceforge.guacamole.net.basic.event.SessionListenerCollection;
16 import net.sourceforge.guacamole.net.basic.properties.BasicGuacamoleProperties;
17 import net.sourceforge.guacamole.net.event.AuthenticationFailureEvent;
18 import net.sourceforge.guacamole.net.event.AuthenticationSuccessEvent;
19 import net.sourceforge.guacamole.net.event.listener.AuthenticationFailureListener;
20 import net.sourceforge.guacamole.net.event.listener.AuthenticationSuccessListener;
21 import net.sourceforge.guacamole.properties.GuacamoleProperties;
22 import net.sourceforge.guacamole.protocol.GuacamoleConfiguration;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
27 * Abstract servlet which provides an authenticatedService() function that
28 * is only called if the HTTP request is authenticated, or the current
29 * HTTP session has already been authenticated.
31 * Authorized configurations are retrieved using the authentication provider
32 * defined in guacamole.properties. The authentication provider has access
33 * to the request and session, in addition to any submitted username and
34 * password, in order to authenticate the user.
36 * All authorized configurations will be stored in the current HttpSession.
38 * Success and failure are logged.
40 * @author Michael Jumper
42 public abstract class AuthenticatingHttpServlet extends HttpServlet {
44 private Logger logger = LoggerFactory.getLogger(AuthenticatingHttpServlet.class);
47 * The error message to be provided to the client user if authentication
48 * fails for ANY REASON.
50 private static final String AUTH_ERROR_MESSAGE =
51 "User not logged in or authentication failed.";
54 * The AuthenticationProvider to use to authenticate all requests.
56 private AuthenticationProvider authProvider;
59 public void init() throws ServletException {
61 // Get auth provider instance
63 authProvider = GuacamoleProperties.getRequiredProperty(BasicGuacamoleProperties.AUTH_PROVIDER);
65 catch (GuacamoleException e) {
66 logger.error("Error getting authentication provider from properties.", e);
67 throw new ServletException(e);
73 * Notifies all listeners in the given collection that authentication has
76 * @param listeners A collection of all listeners that should be notified.
77 * @param credentials The credentials associated with the authentication
78 * request that failed.
80 private void notifyFailed(Collection listeners, Credentials credentials) {
82 // Build event for auth failure
83 AuthenticationFailureEvent event = new AuthenticationFailureEvent(credentials);
85 // Notify all listeners
86 for (Object listener : listeners) {
88 if (listener instanceof AuthenticationFailureListener)
89 ((AuthenticationFailureListener) listener).authenticationFailed(event);
91 catch (GuacamoleException e) {
92 logger.error("Error notifying AuthenticationFailureListener.", e);
99 * Notifies all listeners in the given collection that authentication was
102 * @param listeners A collection of all listeners that should be notified.
103 * @param credentials The credentials associated with the authentication
104 * request that succeeded.
105 * @return true if all listeners are allowing the authentication success,
106 * or if there are no listeners, and false if any listener is
107 * canceling the authentication success. Note that once one
108 * listener cancels, no other listeners will run.
109 * @throws GuacamoleException If any listener throws an error while being
110 * notified. Note that if any listener throws an
111 * error, the success is canceled, and no other
112 * listeners will run.
114 private boolean notifySuccess(Collection listeners, Credentials credentials)
115 throws GuacamoleException {
117 // Build event for auth success
118 AuthenticationSuccessEvent event = new AuthenticationSuccessEvent(credentials);
120 // Notify all listeners
121 for (Object listener : listeners) {
122 if (listener instanceof AuthenticationSuccessListener) {
124 // Cancel immediately if hook returns false
125 if (!((AuthenticationSuccessListener) listener).authenticationSucceeded(event))
136 * Sends a predefined, generic error message to the user, along with a
137 * "403 - Forbidden" HTTP status code in the response.
139 * @param response The response to send the error within.
140 * @throws IOException If an error occurs while sending the error.
142 private void failAuthentication(HttpServletResponse response) throws IOException {
143 response.setHeader("X-Guacamole-Error-Message", AUTH_ERROR_MESSAGE);
144 response.sendError(HttpServletResponse.SC_FORBIDDEN);
148 protected void service(HttpServletRequest request, HttpServletResponse response)
149 throws IOException, ServletException {
151 HttpSession httpSession = request.getSession(true);
153 // Try to get configs from session
154 Map<String, GuacamoleConfiguration> configs =
155 (Map<String, GuacamoleConfiguration>) httpSession.getAttribute("GUAC_CONFIGS");
157 // If no configs, try to authenticate the user to get the configs using
159 if (configs == null) {
161 SessionListenerCollection listeners;
163 listeners = new SessionListenerCollection(httpSession);
165 catch (GuacamoleException e) {
166 logger.error("Failed to retrieve listeners. Authentication canceled.", e);
167 failAuthentication(response);
171 // Retrieve username and password from parms
172 String username = request.getParameter("username");
173 String password = request.getParameter("password");
175 // Build credentials object
176 Credentials credentials = new Credentials ();
177 credentials.setSession(httpSession);
178 credentials.setRequest(request);
179 credentials.setUsername(username);
180 credentials.setPassword(password);
182 // Get authorized configs
184 configs = authProvider.getAuthorizedConfigurations(credentials);
188 /******** HANDLE FAILED AUTHENTICATION ********/
190 // If error retrieving configs, fail authentication, notify listeners
191 catch (GuacamoleException e) {
192 logger.error("Error retrieving configuration(s) for user \"{}\".", username);
194 notifyFailed(listeners, credentials);
195 failAuthentication(response);
199 // If no configs, fail authentication, notify listeners
200 if (configs == null) {
201 logger.warn("Authentication attempt from {} for user \"{}\" failed.",
202 request.getRemoteAddr(), username);
204 notifyFailed(listeners, credentials);
205 failAuthentication(response);
210 /******** HANDLE SUCCESSFUL AUTHENTICATION ********/
214 // Otherwise, authentication has been succesful
215 logger.info("User \"{}\" successfully authenticated from {}.",
216 username, request.getRemoteAddr());
218 // Notify of success, cancel if requested
219 if (!notifySuccess(listeners, credentials)) {
220 logger.info("Successful authentication canceled by hook.");
221 failAuthentication(response);
226 catch (GuacamoleException e) {
228 // Cancel authentication success if hook throws exception
229 logger.error("Successful authentication canceled by error in hook.");
230 failAuthentication(response);
235 // Associate configs with session
236 httpSession.setAttribute("GUAC_CONFIGS", configs);
241 // Allow servlet to run now that authentication has been validated
242 authenticatedService(configs, request, response);
246 protected abstract void authenticatedService(
247 Map<String, GuacamoleConfiguration> configs,
248 HttpServletRequest request, HttpServletResponse response)
249 throws ServletException, IOException;