GUACAMOLE-96: Invoke decorate() for all AuthenticationProviders when creating or updating the UserContext.
Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/a745569f Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/a745569f Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/a745569f Branch: refs/heads/master Commit: a745569f1303dd2f1f4a16190096680d53dbc9cd Parents: 41059f5 Author: Michael Jumper <mjum...@apache.org> Authored: Fri Oct 27 14:30:42 2017 -0700 Committer: Michael Jumper <mjum...@apache.org> Committed: Sat Jan 13 17:23:08 2018 -0800 ---------------------------------------------------------------------- .../org/apache/guacamole/GuacamoleSession.java | 13 +- .../rest/auth/AuthenticationService.java | 25 ++-- .../rest/auth/DecoratedUserContext.java | 147 +++++++++++++++++++ .../guacamole/rest/auth/TokenRESTService.java | 2 +- 4 files changed, 170 insertions(+), 17 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/a745569f/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java ---------------------------------------------------------------------- diff --git a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java index e723c0a..24ea196 100644 --- a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java +++ b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java @@ -28,6 +28,7 @@ import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.rest.auth.DecoratedUserContext; import org.apache.guacamole.tunnel.UserTunnel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,7 +53,7 @@ public class GuacamoleSession { * All UserContexts associated with this session. Each * AuthenticationProvider may provide its own UserContext. */ - private List<UserContext> userContexts; + private List<DecoratedUserContext> userContexts; /** * All currently-active tunnels, indexed by tunnel UUID. @@ -84,7 +85,7 @@ public class GuacamoleSession { */ public GuacamoleSession(Environment environment, AuthenticatedUser authenticatedUser, - List<UserContext> userContexts) + List<DecoratedUserContext> userContexts) throws GuacamoleException { this.lastAccessedTime = System.currentTimeMillis(); this.authenticatedUser = authenticatedUser; @@ -121,7 +122,7 @@ public class GuacamoleSession { * An unmodifiable list of all UserContexts associated with this * session. */ - public List<UserContext> getUserContexts() { + public List<DecoratedUserContext> getUserContexts() { return Collections.unmodifiableList(userContexts); } @@ -141,12 +142,12 @@ public class GuacamoleSession { * @throws GuacamoleException * If no such UserContext exists. */ - public UserContext getUserContext(String authProviderIdentifier) + public DecoratedUserContext getUserContext(String authProviderIdentifier) throws GuacamoleException { // Locate and return the UserContext associated with the // AuthenticationProvider having the given identifier, if any - for (UserContext userContext : getUserContexts()) { + for (DecoratedUserContext userContext : getUserContexts()) { // Get AuthenticationProvider associated with current UserContext AuthenticationProvider authProvider = userContext.getAuthenticationProvider(); @@ -170,7 +171,7 @@ public class GuacamoleSession { * @param userContexts * The List of UserContexts to associate with this session. */ - public void setUserContexts(List<UserContext> userContexts) { + public void setUserContexts(List<DecoratedUserContext> userContexts) { this.userContexts = userContexts; } http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/a745569f/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java ---------------------------------------------------------------------- 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 fb118e1..b6572f8 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 @@ -343,26 +343,30 @@ public class AuthenticationService { * @throws GuacamoleException * If an error occurs while creating or updating any UserContext. */ - private List<UserContext> getUserContexts(GuacamoleSession existingSession, + private List<DecoratedUserContext> getUserContexts(GuacamoleSession existingSession, AuthenticatedUser authenticatedUser, Credentials credentials) throws GuacamoleException { - List<UserContext> userContexts = new ArrayList<UserContext>(authProviders.size()); + List<DecoratedUserContext> userContexts = + new ArrayList<DecoratedUserContext>(authProviders.size()); // If UserContexts already exist, update them and add to the list if (existingSession != null) { // Update all old user contexts - List<UserContext> oldUserContexts = existingSession.getUserContexts(); - for (UserContext oldUserContext : oldUserContexts) { + List<DecoratedUserContext> oldUserContexts = existingSession.getUserContexts(); + for (DecoratedUserContext userContext : oldUserContexts) { + + UserContext oldUserContext = userContext.getOriginal(); // Update existing UserContext AuthenticationProvider authProvider = oldUserContext.getAuthenticationProvider(); - UserContext userContext = authProvider.updateUserContext(oldUserContext, authenticatedUser, credentials); + UserContext updatedUserContext = authProvider.updateUserContext(oldUserContext, authenticatedUser, credentials); // Add to available data, if successful - if (userContext != null) - userContexts.add(userContext); + if (updatedUserContext != null) + userContexts.add(new DecoratedUserContext(updatedUserContext, + authenticatedUser, credentials, authProviders)); // If unsuccessful, log that this happened, as it may be a bug else @@ -384,7 +388,8 @@ public class AuthenticationService { // Add to available data, if successful if (userContext != null) - userContexts.add(userContext); + userContexts.add(new DecoratedUserContext(userContext, + authenticatedUser, credentials, authProviders)); } @@ -428,7 +433,7 @@ public class AuthenticationService { // Get up-to-date AuthenticatedUser and associated UserContexts AuthenticatedUser authenticatedUser = getAuthenticatedUser(existingSession, credentials); - List<UserContext> userContexts = getUserContexts(existingSession, authenticatedUser, credentials); + List<DecoratedUserContext> userContexts = getUserContexts(existingSession, authenticatedUser, credentials); // Update existing session, if it exists String authToken; @@ -513,7 +518,7 @@ public class AuthenticationService { * @throws GuacamoleException * If the auth token does not correspond to any logged in user. */ - public List<UserContext> getUserContexts(String authToken) + public List<DecoratedUserContext> getUserContexts(String authToken) throws GuacamoleException { return getGuacamoleSession(authToken).getUserContexts(); } http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/a745569f/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecoratedUserContext.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..69b6846 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecoratedUserContext.java @@ -0,0 +1,147 @@ +/* + * 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 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.DelegatingUserContext; +import org.apache.guacamole.net.auth.UserContext; + +/** + * A UserContext which has been decorated by all applicable + * AuthenticationProviders. + */ +public class DecoratedUserContext extends DelegatingUserContext { + + /** + * The original, undecorated UserContext. + */ + private final UserContext original; + + /** + * 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. + * + * @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. + * + * @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. + * + * @throws GuacamoleException + * If any of the given AuthenticationProviders fails while decorating + * the UserContext. + */ + private static UserContext decorate(UserContext userContext, + AuthenticatedUser authenticatedUser, Credentials credentials, + List<AuthenticationProvider> authProviders) throws GuacamoleException { + + AuthenticationProvider owner = 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 + UserContext decorated = authProvider.decorate(userContext, + authenticatedUser, credentials); + + // Do not allow misbehaving extensions to wipe out the + // UserContext entirely + if (decorated != null) + userContext = decorated; + + } + + return userContext; + + } + + /** + * 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. + * + * @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. + * + * @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(UserContext userContext, + AuthenticatedUser authenticatedUser, Credentials credentials, + List<AuthenticationProvider> authProviders) throws GuacamoleException { + super(decorate(userContext, authenticatedUser, credentials, authProviders)); + this.original = userContext; + } + + /** + * Returns the original, undecorated UserContext, as provided to the + * constructor of this DecoratedUserContext. + * + * @return + * The original, undecorated UserContext. + */ + public UserContext getOriginal() { + return original; + } + +} http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/a745569f/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java ---------------------------------------------------------------------- diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java index 6366d28..cea0315 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java @@ -186,7 +186,7 @@ public class TokenRESTService { throw new GuacamoleResourceNotFoundException("No such token."); // Build list of all available auth providers - List<UserContext> userContexts = session.getUserContexts(); + List<DecoratedUserContext> userContexts = session.getUserContexts(); List<String> authProviderIdentifiers = new ArrayList<String>(userContexts.size()); for (UserContext userContext : userContexts) authProviderIdentifiers.add(userContext.getAuthenticationProvider().getIdentifier());