diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/AuthenticationSessionManager.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/AuthenticationSessionManager.java index 6e13144c2..11ef307e2 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/AuthenticationSessionManager.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/AuthenticationSessionManager.java @@ -19,8 +19,9 @@ package org.apache.guacamole.auth.sso; -import com.google.common.base.Predicates; import com.google.inject.Inject; +import com.google.inject.Singleton; + import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -38,6 +39,7 @@ import java.util.concurrent.TimeUnit; * @param * The type of sessions managed by this session manager. */ +@Singleton public class AuthenticationSessionManager { /** @@ -152,7 +154,8 @@ public class AuthenticationSessionManager { */ public T resume(String identifier) { if (identifier != null) { - T session = sessions.get(identifier); + + T session = sessions.get(identifier); // Mark the session as pending. NOTE: Unless explicitly removed // from pending status via a call to reactivateSession(), diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/SSOAuthenticationEventListener.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/SSOAuthenticationEventListener.java new file mode 100644 index 000000000..46df4fd49 --- /dev/null +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/SSOAuthenticationEventListener.java @@ -0,0 +1,115 @@ +/* + * 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.auth.sso; + +import org.apache.guacamole.GuacamoleClientException; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.net.auth.Credentials; +import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; +import org.apache.guacamole.net.event.AuthenticationFailureEvent; +import org.apache.guacamole.net.event.AuthenticationRequestReceivedEvent; +import org.apache.guacamole.net.event.CredentialEvent; +import org.apache.guacamole.net.event.listener.Listener; + +/** + * A Listener that will reactivate or invalidate SSO auth sessions depending on + * overall auth success or failure. + */ +public abstract class SSOAuthenticationEventListener implements Listener { + + @Override + public void handleEvent(Object event) throws GuacamoleException { + + // If the authentication attempt is incomplete or credentials cannot be + // extracted, there's nothing to do + if (event instanceof AuthenticationRequestReceivedEvent + || !(event instanceof CredentialEvent)) + return; + + // Look for a session identifier associated with these credentials + String sessionIdentifier = getSessionIdentifier( + ((CredentialEvent) event).getCredentials()); + + // If no session is associated with these credentials, there's + // nothing to do + if (sessionIdentifier == null) + return; + + // If the SSO auth succeeded, but other auth providers failed to + // authenticate the user associated with the credentials in this + // failure event, they may wish to make another login attempt. To + // avoid an infinite login attempt loop, re-enable the session + // associated with these credentials, allowing the auth attempt to be + // resumed without requiring another round trip to the SSO service. + if (event instanceof AuthenticationFailureEvent) { + Throwable failure = ((AuthenticationFailureEvent) event).getFailure(); + + // If and only if the failure was associated with missing or + // credentials, or a non-security related request issue, + // reactivate the session + if (failure instanceof GuacamoleInsufficientCredentialsException + || ((failure instanceof GuacamoleClientException) + && !(failure instanceof GuacamoleSecurityException))) { + + reactivateSession(sessionIdentifier); + return; + + } + + } + + // Invalidate the session in all other cases + invalidateSession(sessionIdentifier); + + } + + /** + * Get the session identifier associated with the provided credentials, + * if any. If no session is associated with the credentials, null will + * be returned. + * + * @param credentials + * The credentials assoociated with the deferred SSO authentication + * session to reactivate. + * + * @return + * The session identifier associated with the provided credentials, + * or null if no session is found. + */ + protected abstract String getSessionIdentifier(Credentials credentials); + + /** + * Reactivate the session identified by the provided identifier, if any. + * + * @param sessionIdentifier + * The identifier of the session to reactivate. + */ + protected abstract void reactivateSession(String sessionIdentifier); + + /** + * Invalidate the session identified by the provided identifier, if any. + * + * @param sessionIdentifier + * The identifier of the session to invalidate. + */ + protected abstract void invalidateSession(String sessionIdentifier); + +} diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationEventListener.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationEventListener.java deleted file mode 100644 index bde671ba1..000000000 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationEventListener.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.auth.saml; - -import com.google.inject.Inject; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.auth.saml.acs.SAMLAuthenticationSessionManager; -import org.apache.guacamole.net.event.AuthenticationFailureEvent; -import org.apache.guacamole.net.event.AuthenticationSuccessEvent; -import org.apache.guacamole.net.event.listener.Listener; - -/** - * A Listener that will reactivate or invalidate SAML auth sessions depending on - * overall auth success or failure. - */ -public class AuthenticationEventListener implements Listener { - - /** - * Manager of active SAML authentication attempts. - * - * Requires static injection due to the fact that the webapp just calls the - * constructor directly when creating new Listeners. The instances will not - * be constructed by guice. - */ - @Inject - private static SAMLAuthenticationSessionManager sessionManager; - - @Override - public void handleEvent(Object event) throws GuacamoleException { - - if (event instanceof AuthenticationSuccessEvent) - - // After an auth attempt has fully succeeded, invalidate the session - // associated with the successful login event so it can't be reused - sessionManager.invalidateSession( - AuthenticationProviderService.getSessionIdentifier( - ((AuthenticationSuccessEvent) event).getCredentials())); - - else if (event instanceof AuthenticationFailureEvent) - - // If the SSL auth succeeded, but other auth providers failed to - // authenticate the user associated with the credentials in this - // failure event, they may wish to make another login attempt. To - // avoid an infinite login attempt loop, re-enable the session - // associated with these credentials, allowing the auth attempt to be - // resumed without requiring another round trip to the SAML provider. - sessionManager.reactivateSession( - AuthenticationProviderService.getSessionIdentifier( - ((AuthenticationFailureEvent) event).getCredentials())); - - } - -} diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java index b22a25972..2c7990c4f 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java @@ -74,11 +74,12 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS * credentials, or null if no session identifier is found in the * credentials. * - * @param credentials The credentials from which to extract the session - * identifier. + * @param credentials + * The credentials from which to extract the session identifier. * - * @return The session identifier associated with the given credentials, or - * null if no identifier is found. + * @return + * The session identifier associated with the given credentials, or + * null if no identifier is found. */ public static String getSessionIdentifier(Credentials credentials) { diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLAuthenticationEventListener.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLAuthenticationEventListener.java new file mode 100644 index 000000000..f6bb3b06c --- /dev/null +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLAuthenticationEventListener.java @@ -0,0 +1,66 @@ +/* + * 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.auth.saml; + +import org.apache.guacamole.auth.saml.acs.SAMLAuthenticationSessionManager; +import org.apache.guacamole.auth.sso.SSOAuthenticationEventListener; +import org.apache.guacamole.net.auth.Credentials; + +import com.google.inject.Inject; + +/** + * A Listener that will reactivate or invalidate SAML auth sessions depending on + * overall auth success or failure. + */ +public class SAMLAuthenticationEventListener extends SSOAuthenticationEventListener { + + /** + * Session manager for generating and maintaining unique tokens to + * represent the authentication flow of a user who has only partially + * authenticated. + * + * Requires static injection due to the fact that the webapp just calls the + * constructor directly when creating new Listeners. The instances will not + * be constructed by guice. + * + * Note that is possible to instead inject an AuthenticationSessionManager + * instance directly into the base class, but this results in different + * instances of the session manager injected here and in the rest of the + * extension, regardless of singleton configuration for the service. + */ + @Inject + protected static SAMLAuthenticationSessionManager sessionManager; + + @Override + protected String getSessionIdentifier(Credentials credentials) { + return AuthenticationProviderService.getSessionIdentifier(credentials); + } + + @Override + protected void reactivateSession(String sessionIdentifier) { + sessionManager.reactivateSession(sessionIdentifier); + } + + @Override + protected void invalidateSession(String sessionIdentifier) { + sessionManager.invalidateSession(sessionIdentifier); + } + +} diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLAuthenticationProviderModule.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLAuthenticationProviderModule.java index 1e193dd51..e01880a62 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLAuthenticationProviderModule.java @@ -37,7 +37,7 @@ public class SAMLAuthenticationProviderModule extends AbstractModule { bind(SAMLAuthenticationSessionManager.class); bind(SAMLService.class); - requestStaticInjection(AuthenticationEventListener.class); + requestStaticInjection(SAMLAuthenticationEventListener.class); } } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/resources/guac-manifest.json index 9319d4983..00cb8c23d 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/resources/guac-manifest.json +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/resources/guac-manifest.json @@ -10,7 +10,7 @@ ], "listeners" : [ - "org.apache.guacamole.auth.saml.AuthenticationEventListener" + "org.apache.guacamole.auth.saml.SAMLAuthenticationEventListener" ], "css" : [ diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/AuthenticationEventListener.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLAuthenticationEventListener.java similarity index 56% rename from extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/AuthenticationEventListener.java rename to extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLAuthenticationEventListener.java index 3ea7bd0c5..26769ced9 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/AuthenticationEventListener.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLAuthenticationEventListener.java @@ -23,6 +23,8 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.ssl.SSLAuthenticationSessionManager; +import org.apache.guacamole.auth.sso.SSOAuthenticationEventListener; +import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.event.AuthenticationFailureEvent; import org.apache.guacamole.net.event.AuthenticationSuccessEvent; import org.apache.guacamole.net.event.listener.Listener; @@ -31,8 +33,7 @@ import org.apache.guacamole.net.event.listener.Listener; * A Listener that will reactivate or invalidate SSL auth sessions depending on * overall auth success or failure. */ -@Singleton -public class AuthenticationEventListener implements Listener { +public class SSLAuthenticationEventListener extends SSOAuthenticationEventListener { /** * Session manager for generating and maintaining unique tokens to @@ -44,31 +45,21 @@ public class AuthenticationEventListener implements Listener { * be constructed by guice. */ @Inject - private static SSLAuthenticationSessionManager sessionManager; + protected static SSLAuthenticationSessionManager sessionManager; @Override - public void handleEvent(Object event) throws GuacamoleException { + protected String getSessionIdentifier(Credentials credentials) { + return AuthenticationProviderService.getSessionIdentifier(credentials); + } - if (event instanceof AuthenticationSuccessEvent) - - // After an auth attempt has fully succeeded, invalidate the session - // associated with the successful login event so it can't be reused - sessionManager.invalidateSession( - AuthenticationProviderService.getSessionIdentifier( - ((AuthenticationSuccessEvent) event).getCredentials())); - - else if (event instanceof AuthenticationFailureEvent) - - // If the SSL auth succeeded, but other auth providers failed to - // authenticate the user associated with the credentials in this - // failure event, they may wish to make another login attempt. To - // avoid an infinite login attempt loop, re-enable the session - // associated with these credentials, allowing the auth attempt to be - // resumed without requiring another round trip to the SSL service. - sessionManager.reactivateSession( - AuthenticationProviderService.getSessionIdentifier( - ((AuthenticationFailureEvent) event).getCredentials())); + @Override + protected void reactivateSession(String sessionIdentifier) { + sessionManager.reactivateSession(sessionIdentifier); + } + @Override + protected void invalidateSession(String sessionIdentifier) { + sessionManager.invalidateSession(sessionIdentifier); } } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLAuthenticationProviderModule.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLAuthenticationProviderModule.java index 36429b0b8..9f7b15aff 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLAuthenticationProviderModule.java @@ -36,7 +36,7 @@ public class SSLAuthenticationProviderModule extends AbstractModule { bind(NonceService.class).in(Scopes.SINGLETON); bind(SSLAuthenticationSessionManager.class); - requestStaticInjection(AuthenticationEventListener.class); + requestStaticInjection(SSLAuthenticationEventListener.class); } } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/resources/guac-manifest.json index 4dc047a90..d5422bf93 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/resources/guac-manifest.json +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/resources/guac-manifest.json @@ -10,7 +10,7 @@ ], "listeners" : [ - "org.apache.guacamole.auth.ssl.AuthenticationEventListener" + "org.apache.guacamole.auth.ssl.SSLAuthenticationEventListener" ], "css" : [