From 5a232f6825deb9b73f89473f941d92d012e36f67 Mon Sep 17 00:00:00 2001 From: Carl Harris Date: Wed, 16 Aug 2017 06:58:18 -0400 Subject: [PATCH] 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();