diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationFailureEvent.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationFailureEvent.java index 9808e7047..870590076 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationFailureEvent.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationFailureEvent.java @@ -19,28 +19,91 @@ package org.apache.guacamole.net.event; +import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Credentials; +import org.apache.guacamole.net.event.listener.Listener; /** * An event which is triggered whenever a user's credentials fail to be * authenticated. The credentials that failed to be authenticated are included * within this event, and can be retrieved using getCredentials(). */ -public class AuthenticationFailureEvent implements CredentialEvent { +public class AuthenticationFailureEvent implements AuthenticationProviderEvent, + CredentialEvent, FailureEvent { /** * The credentials which failed authentication. */ - private Credentials credentials; + private final Credentials credentials; /** - * Creates a new AuthenticationFailureEvent which represents the failure - * to authenticate the given credentials. + * The AuthenticationProvider that encountered the failure. This may be + * null if the AuthenticationProvider is not known, such as if the failure + * is caused by every AuthenticationProvider passively refusing to + * authenticate the user but without explicitly rejecting the user + * (returning null for calls to {@link AuthenticationProvider#authenticateUser(org.apache.guacamole.net.auth.Credentials)}), + * or if the failure is external to any installed AuthenticationProvider + * (such as within a {@link Listener}. + */ + private final AuthenticationProvider authProvider; + + /** + * The Throwable that was thrown resulting in the failure, if any. This + * may be null if authentication failed without a known error, such as if + * the failure is caused by every AuthenticationProvider passively refusing + * to authenticate the user but without explicitly rejecting the user + * (returning null for calls to {@link AuthenticationProvider#authenticateUser(org.apache.guacamole.net.auth.Credentials)}). + */ + private final Throwable failure; + + /** + * Creates a new AuthenticationFailureEvent which represents a failure + * to authenticate the given credentials where there is no specific + * AuthenticationProvider nor Throwable associated with the failure. * - * @param credentials The credentials which failed authentication. + * @param credentials + * The credentials which failed authentication. */ public AuthenticationFailureEvent(Credentials credentials) { + this(credentials, null); + } + + /** + * Creates a new AuthenticationFailureEvent which represents a failure + * to authenticate the given credentials where there is no specific + * AuthenticationProvider causing the failure. + * + * @param credentials + * The credentials which failed authentication. + * + * @param failure + * The Throwable that was thrown resulting in the failure, or null if + * there is no such Throwable. + */ + public AuthenticationFailureEvent(Credentials credentials, Throwable failure) { + this(credentials, null, failure); + } + + /** + * Creates a new AuthenticationFailureEvent which represents a failure + * to authenticate the given credentials. + * + * @param credentials + * The credentials which failed authentication. + * + * @param authProvider + * The AuthenticationProvider that caused the failure, or null if there + * is no such AuthenticationProvider. + * + * @param failure + * The Throwable that was thrown resulting in the failure, or null if + * there is no such Throwable. + */ + public AuthenticationFailureEvent(Credentials credentials, + AuthenticationProvider authProvider, Throwable failure) { this.credentials = credentials; + this.authProvider = authProvider; + this.failure = failure; } @Override @@ -48,4 +111,35 @@ public class AuthenticationFailureEvent implements CredentialEvent { return credentials; } + /** + * {@inheritDoc} + * + *
NOTE: In the case of an authentication failure, cases where this may + * be null include if authentication failed without a definite single + * AuthenticationProvider causing that failure, such as if the failure is + * caused by every AuthenticationProvider passively refusing to + * authenticate the user but without explicitly rejecting the user + * (returning null for calls to {@link AuthenticationProvider#authenticateUser(org.apache.guacamole.net.auth.Credentials)}), + * or if the failure is external to any installed AuthenticationProvider + * (such as within a {@link Listener}. + */ + @Override + public AuthenticationProvider getAuthenticationProvider() { + return authProvider; + } + + /** + * {@inheritDoc} + * + *
NOTE: In the case of an authentication failure, cases where this may
+ * be null include if authentication failed without a known error, such as
+ * if the failure is caused by every AuthenticationProvider passively
+ * refusing to authenticate the user but without explicitly rejecting the
+ * user (returning null for calls to {@link AuthenticationProvider#authenticateUser(org.apache.guacamole.net.auth.Credentials)}).
+ */
+ @Override
+ public Throwable getFailure() {
+ return failure;
+ }
+
}
diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationProviderEvent.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationProviderEvent.java
new file mode 100644
index 000000000..7faa87697
--- /dev/null
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationProviderEvent.java
@@ -0,0 +1,40 @@
+/*
+ * 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.net.event;
+
+import org.apache.guacamole.net.auth.AuthenticationProvider;
+
+/**
+ * An event which may be dispatched due to a specific AuthenticationProvider.
+ */
+public interface AuthenticationProviderEvent {
+
+ /**
+ * Returns the AuthenticationProvider that resulted in the event, if any.
+ * If the event occurred without any definite causing
+ * AuthenticationProvider, this may be null.
+ *
+ * @return
+ * The AuthenticationProvider that resulted in the event, or null if no
+ * such AuthenticationProvider is known.
+ */
+ AuthenticationProvider getAuthenticationProvider();
+
+}
diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationSuccessEvent.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationSuccessEvent.java
index 8b63bcf0e..a9b21dce8 100644
--- a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationSuccessEvent.java
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/AuthenticationSuccessEvent.java
@@ -20,6 +20,7 @@
package org.apache.guacamole.net.event;
import org.apache.guacamole.net.auth.AuthenticatedUser;
+import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
/**
@@ -32,7 +33,8 @@ import org.apache.guacamole.net.auth.Credentials;
* is effectively vetoed and will be subsequently processed as though the
* authentication failed.
*/
-public class AuthenticationSuccessEvent implements UserEvent, CredentialEvent {
+public class AuthenticationSuccessEvent implements UserEvent, CredentialEvent,
+ AuthenticationProviderEvent {
/**
* The AuthenticatedUser identifying the user that successfully
@@ -60,7 +62,12 @@ public class AuthenticationSuccessEvent implements UserEvent, CredentialEvent {
@Override
public Credentials getCredentials() {
- return authenticatedUser.getCredentials();
+ return getAuthenticatedUser().getCredentials();
+ }
+
+ @Override
+ public AuthenticationProvider getAuthenticationProvider() {
+ return getAuthenticatedUser().getAuthenticationProvider();
}
}
diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/event/FailureEvent.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/FailureEvent.java
new file mode 100644
index 000000000..dfc337594
--- /dev/null
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/event/FailureEvent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.net.event;
+
+/**
+ * An event which represents failure of an operation, where that failure may
+ * be associated with a particular Throwable.
+ */
+public interface FailureEvent {
+
+ /**
+ * Returns the Throwable that represents the failure that occurred, if any.
+ * If the failure was recognized but without a definite known error, this
+ * may be null.
+ *
+ * @return
+ * The Throwable that represents the failure that occurred, or null if
+ * no such Throwable is known.
+ */
+ Throwable getFailure();
+
+}
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 ce8a9fb0c..8ed76c6a6 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
@@ -34,7 +34,6 @@ import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
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.GuacamoleInsufficientCredentialsException;
import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
@@ -169,14 +168,15 @@ public class AuthenticationService {
* The AuthenticatedUser given by the highest-priority
* AuthenticationProvider for which the given credentials are valid.
*
- * @throws GuacamoleException
+ * @throws GuacamoleAuthenticationProcessException
* If the given credentials are not valid for any
* AuthenticationProvider, or if an error occurs while authenticating
* the user.
*/
private AuthenticatedUser authenticateUser(Credentials credentials)
- throws GuacamoleException {
+ throws GuacamoleAuthenticationProcessException {
+ AuthenticationProvider failedAuthProvider = null;
GuacamoleCredentialsException authFailure = null;
// Attempt authentication against each AuthenticationProvider
@@ -191,27 +191,29 @@ public class AuthenticationService {
// Insufficient credentials should take precedence
catch (GuacamoleInsufficientCredentialsException e) {
- if (authFailure == null || authFailure instanceof GuacamoleInvalidCredentialsException)
+ if (authFailure == null || authFailure instanceof GuacamoleInvalidCredentialsException) {
+ failedAuthProvider = authProvider;
authFailure = e;
+ }
}
-
+
// Catch other credentials exceptions and assign the first one
catch (GuacamoleCredentialsException e) {
- if (authFailure == null)
+ if (authFailure == null) {
+ failedAuthProvider = authProvider;
authFailure = e;
+ }
+ }
+
+ catch (GuacamoleException | RuntimeException | Error e) {
+ throw new GuacamoleAuthenticationProcessException("User "
+ + "authentication was aborted.", authProvider, e);
}
}
- // If a specific failure occured, rethrow that
- if (authFailure != null)
- throw authFailure;
-
- // Otherwise, request standard username/password
- throw new GuacamoleInvalidCredentialsException(
- "Permission Denied.",
- CredentialsInfo.USERNAME_PASSWORD
- );
+ throw new GuacamoleAuthenticationProcessException("User authentication "
+ + "failed.", failedAuthProvider, authFailure);
}
@@ -230,51 +232,29 @@ public class AuthenticationService {
* A AuthenticatedUser which may have been updated due to re-
* authentication.
*
- * @throws GuacamoleException
+ * @throws GuacamoleAuthenticationProcessException
* If an error prevents the user from being re-authenticated.
*/
private AuthenticatedUser updateAuthenticatedUser(AuthenticatedUser authenticatedUser,
- Credentials credentials) throws GuacamoleException {
+ Credentials credentials) throws GuacamoleAuthenticationProcessException {
// Get original AuthenticationProvider
AuthenticationProvider authProvider = authenticatedUser.getAuthenticationProvider();
- // Re-authenticate the AuthenticatedUser against the original AuthenticationProvider only
- authenticatedUser = authProvider.updateAuthenticatedUser(authenticatedUser, credentials);
- if (authenticatedUser == null)
- throw new GuacamoleSecurityException("User re-authentication failed.");
+ try {
- return authenticatedUser;
+ // Re-authenticate the AuthenticatedUser against the original AuthenticationProvider only
+ authenticatedUser = authProvider.updateAuthenticatedUser(authenticatedUser, credentials);
+ if (authenticatedUser == null)
+ throw new GuacamoleSecurityException("User re-authentication failed.");
- }
+ return authenticatedUser;
- /**
- * Notify all bound listeners that a successful authentication
- * has occurred.
- *
- * @param authenticatedUser
- * The user that was successfully authenticated.
- *
- * @throws GuacamoleException
- * If thrown by a listener.
- */
- private void fireAuthenticationSuccessEvent(AuthenticatedUser authenticatedUser)
- throws GuacamoleException {
- listenerService.handleEvent(new AuthenticationSuccessEvent(authenticatedUser));
- }
+ }
+ catch (GuacamoleException | RuntimeException | Error e) {
+ throw new GuacamoleAuthenticationProcessException("User re-authentication failed.", authProvider, e);
+ }
- /**
- * Notify all bound listeners that an authentication attempt has failed.
- *
- * @param credentials
- * The credentials that failed to authenticate.
- *
- * @throws GuacamoleException
- * If thrown by a listener.
- */
- private void fireAuthenticationFailedEvent(Credentials credentials)
- throws GuacamoleException {
- listenerService.handleEvent(new AuthenticationFailureEvent(credentials));
}
/**
@@ -292,61 +272,23 @@ public class AuthenticationService {
* The AuthenticatedUser associated with the given session and
* credentials.
*
- * @throws GuacamoleException
+ * @throws GuacamoleAuthenticationProcessException
* If an error occurs while authenticating or re-authenticating the
* user.
*/
private AuthenticatedUser getAuthenticatedUser(GuacamoleSession existingSession,
- Credentials credentials) throws GuacamoleException {
-
- try {
-
- // Re-authenticate user if session exists
- if (existingSession != null) {
- AuthenticatedUser updatedUser = updateAuthenticatedUser(
- existingSession.getAuthenticatedUser(), credentials);
- fireAuthenticationSuccessEvent(updatedUser);
- return updatedUser;
- }
-
- // Otherwise, attempt authentication as a new user
- AuthenticatedUser authenticatedUser = AuthenticationService.this.authenticateUser(credentials);
- fireAuthenticationSuccessEvent(authenticatedUser);
-
- if (logger.isInfoEnabled())
- logger.info("User \"{}\" successfully authenticated from {}.",
- authenticatedUser.getIdentifier(),
- getLoggableAddress(credentials.getRequest()));
-
- return authenticatedUser;
+ Credentials credentials) throws GuacamoleAuthenticationProcessException {
+ // Re-authenticate user if session exists
+ if (existingSession != null) {
+ AuthenticatedUser updatedUser = updateAuthenticatedUser(
+ existingSession.getAuthenticatedUser(), credentials);
+ return updatedUser;
}
- // Log and rethrow any authentication errors
- catch (GuacamoleException e) {
-
- fireAuthenticationFailedEvent(credentials);
-
- // Get request and username for sake of logging
- HttpServletRequest request = credentials.getRequest();
- String username = credentials.getUsername();
-
- // Log authentication failures with associated usernames
- if (username != null) {
- if (logger.isWarnEnabled())
- logger.warn("Authentication attempt from {} for user \"{}\" failed.",
- getLoggableAddress(request), username);
- }
-
- // Log anonymous authentication failures
- else if (logger.isDebugEnabled())
- logger.debug("Anonymous authentication attempt from {} failed.",
- getLoggableAddress(request));
-
- // Rethrow exception
- throw e;
-
- }
+ // Otherwise, attempt authentication as a new user
+ AuthenticatedUser authenticatedUser = AuthenticationService.this.authenticateUser(credentials);
+ return authenticatedUser;
}
@@ -371,15 +313,14 @@ public class AuthenticationService {
* A List of all UserContexts associated with the given
* AuthenticatedUser.
*
- * @throws GuacamoleException
+ * @throws GuacamoleAuthenticationProcessException
* If an error occurs while creating or updating any UserContext.
*/
private List