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 session attribute holding the map of configurations.
49 private static final String CONFIGURATIONS_ATTRIBUTE = "GUAC_CONFIGS";
52 * The session attribute holding the credentials authorizing this session.
54 private static final String CREDENTIALS_ATTRIBUTE = "GUAC_CREDS";
57 * The AuthenticationProvider to use to authenticate all requests.
59 private AuthenticationProvider authProvider;
62 public void init() throws ServletException {
64 // Get auth provider instance
66 authProvider = GuacamoleProperties.getRequiredProperty(BasicGuacamoleProperties.AUTH_PROVIDER);
68 catch (GuacamoleException e) {
69 logger.error("Error getting authentication provider from properties.", e);
70 throw new ServletException(e);
76 * Notifies all listeners in the given collection that authentication has
79 * @param listeners A collection of all listeners that should be notified.
80 * @param credentials The credentials associated with the authentication
81 * request that failed.
83 private void notifyFailed(Collection listeners, Credentials credentials) {
85 // Build event for auth failure
86 AuthenticationFailureEvent event = new AuthenticationFailureEvent(credentials);
88 // Notify all listeners
89 for (Object listener : listeners) {
91 if (listener instanceof AuthenticationFailureListener)
92 ((AuthenticationFailureListener) listener).authenticationFailed(event);
94 catch (GuacamoleException e) {
95 logger.error("Error notifying AuthenticationFailureListener.", e);
102 * Notifies all listeners in the given collection that authentication was
105 * @param listeners A collection of all listeners that should be notified.
106 * @param credentials The credentials associated with the authentication
107 * request that succeeded.
108 * @return true if all listeners are allowing the authentication success,
109 * or if there are no listeners, and false if any listener is
110 * canceling the authentication success. Note that once one
111 * listener cancels, no other listeners will run.
112 * @throws GuacamoleException If any listener throws an error while being
113 * notified. Note that if any listener throws an
114 * error, the success is canceled, and no other
115 * listeners will run.
117 private boolean notifySuccess(Collection listeners, Credentials credentials)
118 throws GuacamoleException {
120 // Build event for auth success
121 AuthenticationSuccessEvent event = new AuthenticationSuccessEvent(credentials);
123 // Notify all listeners
124 for (Object listener : listeners) {
125 if (listener instanceof AuthenticationSuccessListener) {
127 // Cancel immediately if hook returns false
128 if (!((AuthenticationSuccessListener) listener).authenticationSucceeded(event))
139 * Sends a predefined, generic error message to the user, along with a
140 * "403 - Forbidden" HTTP status code in the response.
142 * @param response The response to send the error within.
143 * @throws IOException If an error occurs while sending the error.
145 private void failAuthentication(HttpServletResponse response) throws IOException {
146 response.sendError(HttpServletResponse.SC_FORBIDDEN);
150 * Returns the credentials associated with the given session.
152 * @param session The session to retrieve credentials from.
153 * @return The credentials associated with the given session.
155 protected Credentials getCredentials(HttpSession session) {
156 return (Credentials) session.getAttribute(CREDENTIALS_ATTRIBUTE);
160 * Returns the configurations associated with the given session.
162 * @param session The session to retrieve configurations from.
163 * @return The configurations associated with the given session.
165 protected Map<String, GuacamoleConfiguration> getConfigurations(HttpSession session) {
166 return (Map<String, GuacamoleConfiguration>) session.getAttribute(CONFIGURATIONS_ATTRIBUTE);
170 protected void service(HttpServletRequest request, HttpServletResponse response)
171 throws IOException, ServletException {
173 HttpSession httpSession = request.getSession(true);
175 // Try to get configs from session
176 Map<String, GuacamoleConfiguration> configs = getConfigurations(httpSession);
178 // If no configs, try to authenticate the user to get the configs using
180 if (configs == null) {
182 SessionListenerCollection listeners;
184 listeners = new SessionListenerCollection(httpSession);
186 catch (GuacamoleException e) {
187 logger.error("Failed to retrieve listeners. Authentication canceled.", e);
188 failAuthentication(response);
192 // Retrieve username and password from parms
193 String username = request.getParameter("username");
194 String password = request.getParameter("password");
196 // Build credentials object
197 Credentials credentials = new Credentials();
198 credentials.setSession(httpSession);
199 credentials.setRequest(request);
200 credentials.setUsername(username);
201 credentials.setPassword(password);
203 // Get authorized configs
205 configs = authProvider.getAuthorizedConfigurations(credentials);
209 /******** HANDLE FAILED AUTHENTICATION ********/
211 // If error retrieving configs, fail authentication, notify listeners
212 catch (GuacamoleException e) {
213 logger.error("Error retrieving configuration(s) for user \"{}\".",
214 credentials.getUsername(), e);
216 notifyFailed(listeners, credentials);
217 failAuthentication(response);
221 // If no configs, fail authentication, notify listeners
222 if (configs == null) {
223 logger.warn("Authentication attempt from {} for user \"{}\" failed.",
224 request.getRemoteAddr(), credentials.getUsername());
226 notifyFailed(listeners, credentials);
227 failAuthentication(response);
232 /******** HANDLE SUCCESSFUL AUTHENTICATION ********/
236 // Otherwise, authentication has been succesful
237 logger.info("User \"{}\" successfully authenticated from {}.",
238 credentials.getUsername(), request.getRemoteAddr());
240 // Notify of success, cancel if requested
241 if (!notifySuccess(listeners, credentials)) {
242 logger.info("Successful authentication canceled by hook.");
243 failAuthentication(response);
248 catch (GuacamoleException e) {
250 // Cancel authentication success if hook throws exception
251 logger.error("Successful authentication canceled by error in hook.", e);
252 failAuthentication(response);
257 // Associate configs and credentials with session
258 httpSession.setAttribute(CONFIGURATIONS_ATTRIBUTE, configs);
259 httpSession.setAttribute(CREDENTIALS_ATTRIBUTE, credentials);
264 // Allow servlet to run now that authentication has been validated
265 authenticatedService(configs, request, response);
269 protected abstract void authenticatedService(
270 Map<String, GuacamoleConfiguration> configs,
271 HttpServletRequest request, HttpServletResponse response)
272 throws ServletException, IOException;