diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProvider.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProvider.java index a96154ae9..5c80bcaf5 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProvider.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/CASAuthenticationProvider.java @@ -114,6 +114,13 @@ public class CASAuthenticationProvider implements AuthenticationProvider { return context; } + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java index aadc231c5..7d27a75fc 100644 --- a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java +++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java @@ -109,6 +109,13 @@ public class DuoAuthenticationProvider implements AuthenticationProvider { return context; } + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderAuthenticationProvider.java b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderAuthenticationProvider.java index d68c2eee1..ca19b3999 100644 --- a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderAuthenticationProvider.java +++ b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/HTTPHeaderAuthenticationProvider.java @@ -114,6 +114,13 @@ public class HTTPHeaderAuthenticationProvider implements AuthenticationProvider return context; } + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/InjectedAuthenticationProvider.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/InjectedAuthenticationProvider.java index 9113834ef..0e3e84b41 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/InjectedAuthenticationProvider.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/InjectedAuthenticationProvider.java @@ -111,6 +111,13 @@ public abstract class InjectedAuthenticationProvider implements AuthenticationPr return context; } + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProvider.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProvider.java index e7a990ee2..2d8dfd748 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProvider.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPAuthenticationProvider.java @@ -110,6 +110,13 @@ public class LDAPAuthenticationProvider implements AuthenticationProvider { return context; } + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java index d58e7dc72..cf0b96e41 100644 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java @@ -114,6 +114,13 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider { return context; } + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return context; + } + @Override public void shutdown() { // Do nothing diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticationProvider.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticationProvider.java index 7c7f12e1c..fd7d84478 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticationProvider.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticationProvider.java @@ -169,16 +169,16 @@ public interface AuthenticationProvider { Credentials credentials) throws GuacamoleException; /** - * Given a UserContext which originates from a different + * Given a UserContext returned from getUserContext() of a different * AuthenticationProvider, returns a UserContext instance which decorates * (wraps) that UserContext, delegating and overriding implemented * functions as necessary. Each UserContext created via getUserContext() - * or updateUserContext() will be passed to the decorate() functions of all - * other AuthenticationProviders, allowing those AuthenticationProviders - * to augment (or perhaps even limit) the functionality or data provided. + * will be passed to the decorate() functions of all other + * AuthenticationProviders, allowing those AuthenticationProviders to + * augment (or perhaps even limit) the functionality or data provided. * * @param context - * An existing UserContext generated by a different + * An existing UserContext generated by getUserContext() of a different * AuthenticationProvider. * * @param authenticatedUser @@ -202,6 +202,45 @@ public interface AuthenticationProvider { AuthenticatedUser authenticatedUser, Credentials credentials) throws GuacamoleException; + /** + * Given a UserContext returned by updateUserContext() of a different + * AuthenticationProvider, returns a UserContext instance which decorates + * (wraps) that UserContext, delegating and overriding implemented + * functions as necessary. Each UserContext created via updateUserContext() + * will be passed to the decorate() functions of all other + * AuthenticationProviders, allowing those AuthenticationProviders to + * augment (or perhaps even limit) the functionality or data provided. + * + * @param decorated + * The UserContext returned when decorate() was invoked on this + * AuthenticationProvider for the UserContext which was just updated + * via a call to updateUserContext(). + * + * @param context + * An existing UserContext generated by updateUserContext() of a + * different AuthenticationProvider. + * + * @param authenticatedUser + * The AuthenticatedUser object representing the user associated with + * the given UserContext. + * + * @param credentials + * The credentials which were most recently submitted for the given + * AuthenticatedUser. These are not guaranteed to be the same as the + * credentials associated with the AuthenticatedUser object, which are + * the credentials provided when the user originally authenticated. + * + * @return + * A decorated (wrapped) UserContext object, or the original, + * undecorated UserContext. + * + * @throws GuacamoleException + * If the UserContext cannot be decorated due to an error. + */ + UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException; + /** * Frees all resources associated with this AuthenticationProvider. This * function will be automatically invoked when the Guacamole server is diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java index fcacc3c97..b29c2d794 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java @@ -270,6 +270,13 @@ public abstract class SimpleAuthenticationProvider } + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + return decorate(context, authenticatedUser, credentials); + } + @Override public void shutdown() { // Do nothing diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java b/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java index b3169de82..a86893179 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/AuthenticationProviderFacade.java @@ -172,6 +172,21 @@ public class AuthenticationProviderFacade implements AuthenticationProvider { } + @Override + public UserContext redecorate(UserContext decorated, UserContext context, + AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Do nothing if underlying auth provider could not be loaded + if (authProvider == null) + return context; + + // Delegate to underlying auth provider + return authProvider.redecorate(decorated, context, + authenticatedUser, credentials); + + } + @Override public void shutdown() { if (authProvider != null) diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java index 587d8338e..98c5c7a63 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java @@ -33,6 +33,7 @@ import org.codehaus.jackson.jaxrs.JacksonJsonProvider; import org.apache.guacamole.rest.auth.TokenRESTService; import org.apache.guacamole.rest.auth.AuthTokenGenerator; import org.apache.guacamole.rest.auth.AuthenticationService; +import org.apache.guacamole.rest.auth.DecorationService; import org.apache.guacamole.rest.auth.SecureRandomAuthTokenGenerator; import org.apache.guacamole.rest.auth.TokenSessionMap; import org.apache.guacamole.rest.connection.ConnectionModule; @@ -80,6 +81,7 @@ public class RESTServiceModule extends ServletModule { bind(ListenerService.class); bind(AuthenticationService.class); bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); + bind(DecorationService.class); // Automatically translate GuacamoleExceptions for REST methods MethodInterceptor interceptor = new RESTExceptionWrapper(); 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 b6572f87c..7f388572a 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 @@ -78,6 +78,12 @@ public class AuthenticationService { @Inject private AuthTokenGenerator authTokenGenerator; + /** + * Service for applying or reapplying layers of decoration. + */ + @Inject + private DecorationService decorationService; + /** * The service to use to notify registered authentication listeners. */ @@ -357,7 +363,7 @@ public class AuthenticationService { List oldUserContexts = existingSession.getUserContexts(); for (DecoratedUserContext userContext : oldUserContexts) { - UserContext oldUserContext = userContext.getOriginal(); + UserContext oldUserContext = userContext.getUndecoratedUserContext(); // Update existing UserContext AuthenticationProvider authProvider = oldUserContext.getAuthenticationProvider(); @@ -365,8 +371,8 @@ public class AuthenticationService { // Add to available data, if successful if (updatedUserContext != null) - userContexts.add(new DecoratedUserContext(updatedUserContext, - authenticatedUser, credentials, authProviders)); + userContexts.add(decorationService.redecorate(userContext, + updatedUserContext, authenticatedUser, credentials)); // If unsuccessful, log that this happened, as it may be a bug else @@ -388,8 +394,8 @@ public class AuthenticationService { // Add to available data, if successful if (userContext != null) - userContexts.add(new DecoratedUserContext(userContext, - authenticatedUser, credentials, authProviders)); + userContexts.add(decorationService.decorate(userContext, + authenticatedUser, credentials)); } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecoratedUserContext.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecoratedUserContext.java index 69b6846a5..24ad8b35b 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecoratedUserContext.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecoratedUserContext.java @@ -19,7 +19,6 @@ package org.apache.guacamole.rest.auth; -import java.util.List; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; @@ -28,21 +27,38 @@ import org.apache.guacamole.net.auth.DelegatingUserContext; import org.apache.guacamole.net.auth.UserContext; /** - * A UserContext which has been decorated by all applicable - * AuthenticationProviders. + * A UserContext which has been decorated by an AuthenticationProvider through + * invoking decorate() or redecorate(). */ public class DecoratedUserContext extends DelegatingUserContext { /** * The original, undecorated UserContext. */ - private final UserContext original; + private final UserContext undecoratedUserContext; /** - * Repeatedly decorates the given UserContext, invoking the decorate() - * function of each given AuthenticationProvider, wrapping the UserContext - * within successive layers of decoration. The AuthenticationProvider which - * originated the given UserContext will be ignored. + * The AuthenticationProvider which applied this layer of decoration. + */ + private final AuthenticationProvider decoratingAuthenticationProvider; + + /** + * The DecoratedUserContext which applies the layer of decoration + * immediately beneath this DecoratedUserContext. If no further decoration + * has been applied, this will be null. + */ + private final DecoratedUserContext decoratedUserContext; + + /** + * Decorates a newly-created UserContext (as would be returned by + * getUserContext()), invoking the decorate() function of the given + * AuthenticationProvider to apply an additional layer of decoration. If the + * AuthenticationProvider originated the given UserContext, this function + * has no effect. + * + * @param authProvider + * The AuthenticationProvider which should be used to decorate the + * given UserContext. * * @param userContext * The UserContext to decorate. @@ -55,42 +71,87 @@ public class DecoratedUserContext extends DelegatingUserContext { * The credentials associated with the request which produced the given * UserContext. * - * @param authProviders - * The AuthenticationProviders which should be used to decorate the - * given UserContext. The order of this list dictates the order in - * which each AuthenticationProvider's decorate() function will be - * invoked. - * * @return - * A UserContext instance which has been decorated (wrapped) by all - * applicable AuthenticationProviders. + * A UserContext instance which has been decorated (wrapped) by the + * given AuthenticationProvider, or the original UserContext if the + * given AuthenticationProvider originated the UserContext. * * @throws GuacamoleException - * If any of the given AuthenticationProviders fails while decorating - * the UserContext. + * If the given AuthenticationProvider fails while decorating the + * UserContext. */ - private static UserContext decorate(UserContext userContext, - AuthenticatedUser authenticatedUser, Credentials credentials, - List authProviders) throws GuacamoleException { + private static UserContext decorate(AuthenticationProvider authProvider, + UserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { - AuthenticationProvider owner = userContext.getAuthenticationProvider(); + // Skip the AuthenticationProvider which produced the UserContext + // being decorated + if (authProvider != userContext.getAuthenticationProvider()) { - // Poll each AuthenticationProvider to decorate the given UserContext - for (AuthenticationProvider authProvider : authProviders) { - - // Skip the AuthenticationProvider which produced the UserContext - // being decorated - if (authProvider == owner) - continue; - - // Apply next layer of wrapping around UserContext + // Apply layer of wrapping around UserContext UserContext decorated = authProvider.decorate(userContext, authenticatedUser, credentials); // Do not allow misbehaving extensions to wipe out the // UserContext entirely if (decorated != null) - userContext = decorated; + return decorated; + + } + + return userContext; + + } + + /** + * Redecorates an updated UserContext (as would be returned by + * updateUserContext()), invoking the redecorate() function of the given + * AuthenticationProvider to apply an additional layer of decoration. If the + * AuthenticationProvider originated the given UserContext, this function + * has no effect. + * + * @param decorated + * The DecoratedUserContext associated with an older version of the + * given UserContext. + * + * @param userContext + * The new version of the UserContext which should be decorated. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @return + * A UserContext instance which has been decorated (wrapped) by the + * given AuthenticationProvider, or the original UserContext if the + * given AuthenticationProvider originated the UserContext. + * + * @throws GuacamoleException + * If the given AuthenticationProvider fails while decorating the + * UserContext. + */ + private static UserContext redecorate(DecoratedUserContext decorated, + UserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + AuthenticationProvider authProvider = decorated.getDecoratingAuthenticationProvider(); + + // Skip the AuthenticationProvider which produced the UserContext + // being decorated + if (authProvider != userContext.getAuthenticationProvider()) { + + // Apply next layer of wrapping around UserContext + UserContext redecorated = authProvider.redecorate(decorated, + userContext, authenticatedUser, credentials); + + // Do not allow misbehaving extensions to wipe out the + // UserContext entirely + if (redecorated != null) + return redecorated; } @@ -100,13 +161,17 @@ public class DecoratedUserContext extends DelegatingUserContext { /** * Creates a new DecoratedUserContext, invoking the the decorate() function - * of the given AuthenticationProviders to decorate the provided - * UserContext. Decoration by each AuthenticationProvider will occur in the - * order given. Only AuthenticationProviders which did not originate the - * given UserContext will be used. + * of the given AuthenticationProvider to decorate the provided, undecorated + * UserContext. If the AuthenticationProvider originated the given + * UserContext, then the given UserContext is wrapped without any + * decoration. + * + * @param authProvider + * The AuthenticationProvider which should be used to decorate the + * given UserContext. * * @param userContext - * The UserContext to decorate. + * The undecorated UserContext to decorate. * * @param authenticatedUser * The AuthenticatedUser identifying the user associated with the given @@ -116,32 +181,181 @@ public class DecoratedUserContext extends DelegatingUserContext { * The credentials associated with the request which produced the given * UserContext. * - * @param authProviders - * The AuthenticationProviders which should be used to decorate the - * given UserContext. The order of this list dictates the order in - * which each AuthenticationProvider's decorate() function will be - * invoked. + * @throws GuacamoleException + * If any of the given AuthenticationProviders fails while decorating + * the UserContext. + */ + public DecoratedUserContext(AuthenticationProvider authProvider, + UserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Wrap the result of invoking decorate() on the given AuthenticationProvider + super(decorate(authProvider, userContext, authenticatedUser, credentials)); + this.decoratingAuthenticationProvider = authProvider; + + // The wrapped UserContext is undecorated + this.undecoratedUserContext = userContext; + this.decoratedUserContext = null; + + } + + /** + * Creates a new DecoratedUserContext, invoking the the decorate() function + * of the given AuthenticationProvider to apply an additional layer of + * decoration to a DecoratedUserContext. If the AuthenticationProvider + * originated the given UserContext, then the given UserContext is wrapped + * without any decoration. + * + * @param authProvider + * The AuthenticationProvider which should be used to decorate the + * given UserContext. + * + * @param userContext + * The DecoratedUserContext to decorate. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. * * @throws GuacamoleException * If any of the given AuthenticationProviders fails while decorating * the UserContext. */ - public DecoratedUserContext(UserContext userContext, - AuthenticatedUser authenticatedUser, Credentials credentials, - List authProviders) throws GuacamoleException { - super(decorate(userContext, authenticatedUser, credentials, authProviders)); - this.original = userContext; + public DecoratedUserContext(AuthenticationProvider authProvider, + DecoratedUserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Wrap the result of invoking decorate() on the given AuthenticationProvider + super(decorate(authProvider, userContext, authenticatedUser, credentials)); + this.decoratingAuthenticationProvider = authProvider; + + // The wrapped UserContext has at least one layer of decoration + this.undecoratedUserContext = userContext.getUndecoratedUserContext(); + this.decoratedUserContext = userContext; + } /** - * Returns the original, undecorated UserContext, as provided to the - * constructor of this DecoratedUserContext. + * Creates a new DecoratedUserContext, invoking the the redecorate() + * function of the given AuthenticationProvider to reapply decoration to the + * provided, undecorated UserContext, which has been updated relative to a + * past version which was decorated. If the AuthenticationProvider + * originated the given UserContext, then the given UserContext is wrapped + * without any decoration. + * + * @param decorated + * The DecoratedUserContext associated with the older version of the + * given UserContext. + * + * @param userContext + * The undecorated UserContext to decorate. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @throws GuacamoleException + * If any of the given AuthenticationProviders fails while decorating + * the UserContext. + */ + public DecoratedUserContext(DecoratedUserContext decorated, + UserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Wrap the result of invoking redecorate() on the given AuthenticationProvider + super(redecorate(decorated, userContext, authenticatedUser, credentials)); + this.decoratingAuthenticationProvider = decorated.getDecoratingAuthenticationProvider(); + + // The wrapped UserContext is undecorated + this.undecoratedUserContext = userContext; + this.decoratedUserContext = null; + + } + + /** + * Creates a new DecoratedUserContext, invoking the the redecorate() + * function of the given AuthenticationProvider to reapply decoration to a + * DecoratedUserContext which already has at least one layer of decoration + * applied, and which is associated with a UserContext which was updated + * relative to a past version which was decorated. If the + * AuthenticationProvider originated the given UserContext, then the given + * UserContext is wrapped without any decoration. + * + * @param decorated + * The DecoratedUserContext associated with the older version of the + * UserContext wrapped within one or more layers of decoration. + * + * @param userContext + * The DecoratedUserContext to decorate. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @throws GuacamoleException + * If any of the given AuthenticationProviders fails while decorating + * the UserContext. + */ + public DecoratedUserContext(DecoratedUserContext decorated, + DecoratedUserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Wrap the result of invoking redecorate() on the given AuthenticationProvider + super(redecorate(decorated, userContext, authenticatedUser, credentials)); + this.decoratingAuthenticationProvider = decorated.getDecoratingAuthenticationProvider(); + + // The wrapped UserContext has at least one layer of decoration + this.undecoratedUserContext = userContext.getUndecoratedUserContext(); + this.decoratedUserContext = userContext; + + } + + /** + * Returns the original UserContext with absolutely no layers of decoration + * applied. * * @return * The original, undecorated UserContext. */ - public UserContext getOriginal() { - return original; + public UserContext getUndecoratedUserContext() { + return undecoratedUserContext; + } + + /** + * Returns the AuthenticationProvider which applied the layer of decoration + * represented by this DecoratedUserContext. + * + * @return + * The AuthenticationProvider which applied this layer of decoration. + */ + public AuthenticationProvider getDecoratingAuthenticationProvider() { + return decoratingAuthenticationProvider; + } + + /** + * Returns the DecoratedUserContext representing the next layer of + * decoration, itself decorated by this DecoratedUserContext. If no further + * layers of decoration exist, this will be null. + * + * @return + * The DecoratedUserContext which applies the layer of decoration + * immediately beneath this DecoratedUserContext, or null if no further + * decoration has been applied. + */ + public DecoratedUserContext getDecoratedUserContext() { + return decoratedUserContext; } } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecorationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecorationService.java new file mode 100644 index 000000000..b63b037b3 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecorationService.java @@ -0,0 +1,145 @@ +/* + * 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.rest.auth; + +import com.google.inject.Inject; +import java.util.Iterator; +import java.util.List; + +import org.apache.guacamole.GuacamoleException; +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; + +/** + * A service for applying or reapplying layers of decoration to UserContexts. + * The semantics of UserContext decoration/redecoration is defined by the + * AuthenticationProvider interface. + */ +public class DecorationService { + + /** + * All configured authentication providers which can be used to + * authenticate users or retrieve data associated with authenticated users. + */ + @Inject + private List authProviders; + + /** + * Creates a new DecoratedUserContext, invoking the the decorate() function + * of all AuthenticationProviders to decorate the provided UserContext. + * Decoration by each AuthenticationProvider will occur in the order that + * the AuthenticationProviders were loaded. Only AuthenticationProviders + * which did not originate the given UserContext will be used. + * + * @param userContext + * The UserContext to decorate. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @return + * A new DecoratedUserContext which has been decorated by all + * AuthenticationProviders. + * + * @throws GuacamoleException + * If any AuthenticationProvider fails while decorating the UserContext. + */ + public DecoratedUserContext decorate(UserContext userContext, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + + // Get first AuthenticationProvider in list + Iterator current = authProviders.iterator(); + if (!current.hasNext()) + return null; + + // Use first AuthenticationProvider to produce the root-level + // decorated UserContext + DecoratedUserContext decorated = new DecoratedUserContext(current.next(), + userContext, authenticatedUser, credentials); + + // Repeatedly wrap the decorated UserContext with additional layers of + // decoration for each remaining AuthenticationProvider + while (current.hasNext()) { + decorated = new DecoratedUserContext(current.next(), decorated, + authenticatedUser, credentials); + } + + return decorated; + + } + + /** + * Creates a new DecoratedUserContext, invoking the the redecorate() + * function of all AuthenticationProviders to reapply decoration. Decoration + * by each AuthenticationProvider will occur in the order that the + * AuthenticationProviders were loaded. Only AuthenticationProviders which + * did not originate the given UserContext will be used. + * + * @param decorated + * The DecoratedUserContext associated with an older version of the + * given UserContext. + * + * @param userContext + * The new version of the UserContext which should be decorated. + * + * @param authenticatedUser + * The AuthenticatedUser identifying the user associated with the given + * UserContext. + * + * @param credentials + * The credentials associated with the request which produced the given + * UserContext. + * + * @return + * A new DecoratedUserContext which has been decorated by all + * AuthenticationProviders. + * + * @throws GuacamoleException + * If any AuthenticationProvider fails while decorating the UserContext. + */ + public DecoratedUserContext redecorate(DecoratedUserContext decorated, + UserContext userContext, AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // If the given DecoratedUserContext contains further decorated layers, + // redecorate those first + DecoratedUserContext next = decorated.getDecoratedUserContext(); + if (next != null) { + return new DecoratedUserContext(decorated, + redecorate(next, userContext, authenticatedUser, credentials), + authenticatedUser, credentials); + } + + // If only one layer of decoration is present, simply redecorate that + // layer + return new DecoratedUserContext(decorated, userContext, + authenticatedUser, credentials); + + } + +}