diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java index 40de6e32a..4c5877fc5 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java @@ -25,6 +25,8 @@ import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition; +import org.apache.guacamole.auth.jdbc.sharing.user.SharedAuthenticatedUser; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileService; import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord; @@ -128,11 +130,11 @@ public class ConnectionSharingService { } /** - * Returns a SharedConnectionUser (an implementation of AuthenticatedUser) - * if the given credentials contain a valid share key. The returned user - * will be associated with the single shared connection to which they have - * been granted temporary access. If the share key is invalid, or no share - * key is contained within the given credentials, null is returned. + * Returns a SharedAuthenticatedUser if the given credentials contain a + * valid share key. The returned user will be associated with the single + * shared connection to which they have been granted temporary access. If + * the share key is invalid, or no share key is contained within the given + * credentials, null is returned. * * @param authProvider * The AuthenticationProvider on behalf of which the user is being @@ -142,11 +144,11 @@ public class ConnectionSharingService { * The credentials which are expected to contain the share key. * * @return - * A SharedConnectionUser with access to a single shared connection, if - * the share key within the given credentials is valid, or null if the - * share key is invalid or absent. + * A SharedAuthenticatedUser with access to a single shared connection, + * if the share key within the given credentials is valid, or null if + * the share key is invalid or absent. */ - public SharedConnectionUser retrieveSharedConnectionUser( + public SharedAuthenticatedUser retrieveSharedConnectionUser( AuthenticationProvider authProvider, Credentials credentials) { // Pull associated HTTP request @@ -159,14 +161,12 @@ public class ConnectionSharingService { if (shareKey == null) return null; - // Pull the connection definition describing the connection these - // credentials provide access to (if any) - SharedConnectionDefinition definition = connectionMap.get(shareKey); - if (definition == null) + // Validate the share key + if (connectionMap.get(shareKey) == null) return null; - // Return temporary in-memory user with access only to the shared connection - return new SharedConnectionUser(authProvider, definition, credentials); + // Return temporary in-memory user + return new SharedAuthenticatedUser(authProvider, credentials, shareKey); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/HashSharedConnectionMap.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/HashSharedConnectionMap.java index ddd812b3b..959d931b6 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/HashSharedConnectionMap.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/HashSharedConnectionMap.java @@ -21,6 +21,7 @@ package org.apache.guacamole.auth.jdbc.sharing; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition; /** * A HashMap-based implementation of the SharedConnectionMap. diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedAuthenticationProviderService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedAuthenticationProviderService.java index cd4e6c2aa..ddcd92901 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedAuthenticationProviderService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedAuthenticationProviderService.java @@ -23,6 +23,8 @@ import com.google.inject.Inject; import com.google.inject.Provider; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.jdbc.AuthenticationProviderService; +import org.apache.guacamole.auth.jdbc.sharing.user.SharedAuthenticatedUser; +import org.apache.guacamole.auth.jdbc.sharing.user.SharedUserContext; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Credentials; @@ -31,8 +33,8 @@ import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsExce /** * Service which authenticates users based on share keys and provides for the - * creation of corresponding. The created UserContext objects are restricted to - * the connections associated with those share keys via a common + * creation of corresponding UserContexts. The created UserContext objects are + * restricted to the connections associated with those share keys via a common * ConnectionSharingService. * * @author Michael Jumper @@ -40,10 +42,10 @@ import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsExce public class SharedAuthenticationProviderService implements AuthenticationProviderService { /** - * Provider for retrieving SharedConnectionUserContext instances. + * Provider for retrieving SharedUserContext instances. */ @Inject - private Provider sharedUserContextProvider; + private Provider sharedUserContextProvider; /** * Service for sharing active connections. @@ -66,19 +68,29 @@ public class SharedAuthenticationProviderService implements AuthenticationProvid } @Override - public org.apache.guacamole.net.auth.UserContext getUserContext( + public SharedUserContext getUserContext( AuthenticationProvider authenticationProvider, AuthenticatedUser authenticatedUser) throws GuacamoleException { - // Produce sharing-specific user context if this is the user of a shared connection - if (authenticatedUser instanceof SharedConnectionUser) { - SharedConnectionUserContext context = sharedUserContextProvider.get(); - context.init((SharedConnectionUser) authenticatedUser); - return context; - } + // Obtain a reference to a correct AuthenticatedUser which can be used + // for accessing shared connections + SharedAuthenticatedUser sharedAuthenticatedUser; + if (authenticatedUser instanceof SharedAuthenticatedUser) + sharedAuthenticatedUser = (SharedAuthenticatedUser) authenticatedUser; + else + sharedAuthenticatedUser = new SharedAuthenticatedUser(authenticatedUser); - // No shared connections otherwise - return null; + // Produce empty user context for known-authenticated user + SharedUserContext context = sharedUserContextProvider.get(); + context.init(authenticationProvider, sharedAuthenticatedUser); + + // Add the shared connection associated with the originally-provided + // share key (if any) + String shareKey = sharedAuthenticatedUser.getShareKey(); + if (shareKey != null) + context.registerShareKey(shareKey); + + return context; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionMap.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionMap.java index 29bce5050..e33bc2d3b 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionMap.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionMap.java @@ -19,6 +19,8 @@ package org.apache.guacamole.auth.jdbc.sharing; +import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition; + /** * Represents a mapping between share keys and the Guacamole connection being * shared. diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUser.java deleted file mode 100644 index 125628a4c..000000000 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUser.java +++ /dev/null @@ -1,93 +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.jdbc.sharing; - -import java.util.UUID; -import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser; -import org.apache.guacamole.net.auth.AuthenticationProvider; -import org.apache.guacamole.net.auth.Credentials; - -/** - * A temporary user who has authenticated using a share key and thus has - * restricted access to a single shared connection. - * - * @author Michael Jumper - */ -public class SharedConnectionUser extends RemoteAuthenticatedUser { - - /** - * The single shared connection to which this user has access. - */ - private final SharedConnectionDefinition definition; - - /** - * An arbitrary identifier guaranteed to be unique across users. Note that - * because Guacamole users the AuthenticatedUser's identifier as the means - * of determining overall user identity and aggregating data across - * multiple extensions, this identifier MUST NOT match the identifier of - * any possibly existing user (or else the user may unexpectedly gain - * access to another identically-named user's data). - */ - private final String identifier = UUID.randomUUID().toString(); - - /** - * Creates a new SharedConnectionUser with access solely to connection - * described by the given SharedConnectionDefinition. - * - * @param authenticationProvider - * The AuthenticationProvider that has authenticated the given user. - * - * @param definition - * The SharedConnectionDefinition describing the connection that this - * user should have access to, along with any associated restrictions. - * - * @param credentials - * The credentials given by the user when they authenticated. - */ - public SharedConnectionUser(AuthenticationProvider authenticationProvider, - SharedConnectionDefinition definition, Credentials credentials) { - super(authenticationProvider, credentials); - this.definition = definition; - } - - @Override - public String getIdentifier() { - return identifier; - } - - @Override - public void setIdentifier(String identifier) { - throw new UnsupportedOperationException("Shared connection users are immutable"); - } - - /** - * Returns the SharedConnectionDefinition which describes the connection - * that this user should have access to, along with any associated - * restrictions. - * - * @return - * The SharedConnectionDefinition describing the connection that this - * user should have access to, along with any associated restrictions. - */ - public SharedConnectionDefinition getSharedConnectionDefinition() { - return definition; - } - -} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnection.java similarity index 88% rename from extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnection.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnection.java index 18862dc19..9ced9c747 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnection.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnection.java @@ -17,17 +17,17 @@ * under the License. */ -package org.apache.guacamole.auth.jdbc.sharing; +package org.apache.guacamole.auth.jdbc.sharing.connection; import com.google.inject.Inject; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.UUID; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup; +import org.apache.guacamole.auth.jdbc.sharing.connectiongroup.SharedRootConnectionGroup; import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; +import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.ConnectionRecord; @@ -48,18 +48,11 @@ public class SharedConnection implements Connection { @Inject private GuacamoleTunnelService tunnelService; - /** - * Randomly-generated unique identifier, guaranteeing this shared connection - * does not duplicate the identifying information of the underlying - * connection being shared. - */ - private final String identifier = UUID.randomUUID().toString(); - /** * The user that successfully authenticated to obtain access to this * SharedConnection. */ - private SharedConnectionUser user; + private RemoteAuthenticatedUser user; /** * The SharedConnectionDefinition dictating the connection being shared and @@ -79,14 +72,14 @@ public class SharedConnection implements Connection { * The SharedConnectionDefinition dictating the connection being shared * and any associated restrictions. */ - public void init(SharedConnectionUser user, SharedConnectionDefinition definition) { + public void init(RemoteAuthenticatedUser user, SharedConnectionDefinition definition) { this.user = user; this.definition = definition; } @Override public String getIdentifier() { - return identifier; + return definition.getShareKey(); } @Override @@ -106,7 +99,7 @@ public class SharedConnection implements Connection { @Override public String getParentIdentifier() { - return RootConnectionGroup.IDENTIFIER; + return SharedRootConnectionGroup.IDENTIFIER; } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionDefinition.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnectionDefinition.java similarity index 97% rename from extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionDefinition.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnectionDefinition.java index d4a6b7e8f..bceda2537 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionDefinition.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnectionDefinition.java @@ -17,9 +17,10 @@ * under the License. */ -package org.apache.guacamole.auth.jdbc.sharing; +package org.apache.guacamole.auth.jdbc.sharing.connection; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.sharing.SharedObjectManager; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord; import org.apache.guacamole.net.GuacamoleTunnel; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnectionDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnectionDirectory.java new file mode 100644 index 000000000..441203c38 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnectionDirectory.java @@ -0,0 +1,158 @@ +/* + * 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.jdbc.sharing.connection; + + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionMap; +import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser; +import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.net.auth.Directory; + +/** + * A Directory implementation which exposes an explicitly-registered set of + * share keys as connections. Only explicitly-registered share keys are + * accessible a SharedConnectionDirectory. + * + * @author Michael Jumper + */ +public class SharedConnectionDirectory implements Directory { + + /** + * Map of all currently-shared connections. + */ + @Inject + private SharedConnectionMap connectionMap; + + /** + * Provider for retrieving SharedConnection instances. + */ + @Inject + private Provider connectionProvider; + + /** + * The user associated with the UserContext that contains this directory. + */ + private RemoteAuthenticatedUser currentUser; + + /** + * The set of share keys that have been explicitly registered. In general, + * only valid share keys will be present here, but invalid keys are only + * removed after an attempt to retrieve those keys has been made. + */ + private final Set shareKeys = + Collections.newSetFromMap(new ConcurrentHashMap()); + + /** + * Creates a new SharedConnectionDirectory which exposes share keys as + * connections. Only explicitly-registered and valid share keys will be + * accessible. + * + * @param currentUser + * The user associated with the UserContext that will contain this + * directory. + */ + public void init(RemoteAuthenticatedUser currentUser) { + this.currentUser = currentUser; + } + + /** + * Registers a new share key such that the connection associated with that + * share key is accessible through this directory. The share key will be + * automatically de-registered when it is no longer valid. + * + * @param shareKey + * The share key to register. + */ + public void registerShareKey(String shareKey) { + shareKeys.add(shareKey); + } + + @Override + public Connection get(String identifier) throws GuacamoleException { + + // Allow access only to registered share keys + if (!shareKeys.contains(identifier)) + return null; + + // Retrieve the connection definition associated with that share key, + // cleaning up the internally-stored share key if it's no longer valid + SharedConnectionDefinition connectionDefinition = connectionMap.get(identifier); + if (connectionDefinition == null) { + shareKeys.remove(identifier); + return null; + } + + // Return a Connection which wraps that connection definition + SharedConnection connection = connectionProvider.get(); + connection.init(currentUser, connectionDefinition); + return connection; + + } + + @Override + public Collection getAll(Collection identifiers) + throws GuacamoleException { + + // Create collection with enough backing space to contain one + // connection per identifier given + Collection matchingConnections = + new ArrayList(identifiers.size()); + + // Add all connnections which exist according to get() + for (String identifier : identifiers) { + Connection connection = get(identifier); + if (connection != null) + matchingConnections.add(connection); + } + + return Collections.unmodifiableCollection(matchingConnections); + + } + + @Override + public Set getIdentifiers() throws GuacamoleException { + return Collections.unmodifiableSet(shareKeys); + } + + @Override + public void add(Connection object) throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void update(Connection object) throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void remove(String identifier) throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connectiongroup/SharedRootConnectionGroup.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connectiongroup/SharedRootConnectionGroup.java new file mode 100644 index 000000000..0a79230cc --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connectiongroup/SharedRootConnectionGroup.java @@ -0,0 +1,144 @@ +/* + * 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.jdbc.sharing.connectiongroup; + +import org.apache.guacamole.auth.jdbc.sharing.user.SharedUserContext; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.net.GuacamoleTunnel; +import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.net.auth.ConnectionGroup; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.protocol.GuacamoleClientInformation; + +/** + * A ConnectionGroup implementation which contains all connections accessible + * via a given SharedUserContext. The identifier of a SharedRootConnectionGroup + * is statically defined, and all Connections which are intended to be contained + * within an instance of SharedRootConnectionGroup MUST return that identifier + * via getParentIdentifier(). + * + * @author Michael Jumper + */ +public class SharedRootConnectionGroup implements ConnectionGroup { + + /** + * The identifier of the root connection group. All Connections which are + * intended to be contained within an instance of SharedRootConnectionGroup + * MUST return this identifier via getParentIdentifier(). + */ + public static final String IDENTIFIER = "ROOT"; + + /** + * The SharedUserContext through which this connection group is accessible. + */ + private final SharedUserContext userContext; + + /** + * Creates a new SharedRootConnectionGroup which contains all connections + * accessible via the given SharedUserContext. The SharedRootConnectionGroup + * is backed by the SharedUserContext, and any changes to the connections + * within the SharedUserContext are immediately reflected in the + * SharedRootConnectionGroup. + * + * @param userContext + * The SharedUserContext which should back the new + * SharedRootConnectionGroup. + */ + public SharedRootConnectionGroup(SharedUserContext userContext) { + this.userContext = userContext; + } + + @Override + public String getIdentifier() { + return IDENTIFIER; + } + + @Override + public void setIdentifier(String identifier) { + throw new UnsupportedOperationException("The root group is immutable."); + } + + @Override + public String getName() { + return IDENTIFIER; + } + + @Override + public void setName(String name) { + throw new UnsupportedOperationException("The root group is immutable."); + } + + @Override + public String getParentIdentifier() { + return IDENTIFIER; + } + + @Override + public void setParentIdentifier(String parentIdentifier) { + throw new UnsupportedOperationException("The root group is immutable."); + } + + @Override + public GuacamoleTunnel connect(GuacamoleClientInformation info) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + @Override + public void setAttributes(Map attributes) { + // Do nothing - no attributes supported + } + + @Override + public int getActiveConnections() { + return 0; + } + + @Override + public void setType(Type type) { + throw new UnsupportedOperationException("The root group is immutable."); + } + + @Override + public Type getType() { + return Type.BALANCING; + } + + @Override + public Set getConnectionIdentifiers() throws GuacamoleException { + Directory connectionDirectory = userContext.getConnectionDirectory(); + return connectionDirectory.getIdentifiers(); + } + + @Override + public Set getConnectionGroupIdentifiers() throws GuacamoleException { + return Collections.emptySet(); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/permission/SharedObjectPermissionSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/permission/SharedObjectPermissionSet.java new file mode 100644 index 000000000..18652bf5c --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/permission/SharedObjectPermissionSet.java @@ -0,0 +1,72 @@ +/* + * 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.jdbc.sharing.permission; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import org.apache.guacamole.net.auth.permission.ObjectPermission; +import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet; + +/** + * An immutable ObjectPermissionSet which defines only READ permissions for a + * fixed set of identifiers. + * + * @author Michael Jumper + */ +public class SharedObjectPermissionSet extends SimpleObjectPermissionSet { + + /** + * Returns a new Set of ObjectPermissions defining READ access for each of + * the given identifiers. + * + * @param identifiers + * The identifiers of the objects for which READ permission should be + * granted. + * + * @return + * A new Set of ObjectPermissions granting READ access for each of the + * given identifiers. + */ + private static Set getPermissions(Collection identifiers) { + + // Include one READ permission for each of the given identifiers + Set permissions = new HashSet(); + for (String identifier : identifiers) + permissions.add(new ObjectPermission(ObjectPermission.Type.READ, identifier)); + + return permissions; + + } + + /** + * Creates a new SharedObjectPermissionSet which grants read-only access to + * the objects having the given identifiers. No other permissions are + * granted. + * + * @param identifiers + * The identifiers of the objects for which READ access should be + * granted. + */ + public SharedObjectPermissionSet(Collection identifiers) { + super(getPermissions(identifiers)); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedAuthenticatedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedAuthenticatedUser.java new file mode 100644 index 000000000..a50b62535 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedAuthenticatedUser.java @@ -0,0 +1,105 @@ +/* + * 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.jdbc.sharing.user; + +import java.util.UUID; +import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser; +import org.apache.guacamole.net.auth.AuthenticatedUser; +import org.apache.guacamole.net.auth.AuthenticationProvider; +import org.apache.guacamole.net.auth.Credentials; + +/** + * Associates a user with the credentials they used to authenticate, including + * any provided share key. + * + * @author Michael Jumper + */ +public class SharedAuthenticatedUser extends RemoteAuthenticatedUser { + + /** + * The username of this user. + */ + private final String identifier; + + /** + * The share key which was provided by this user when they authenticated. If + * there is no such share key, this will be null. + */ + private final String shareKey; + + /** + * Creates a new SharedAuthenticatedUser which copies the details of the + * given AuthenticatedUser, including that user's identifier (username). + * The new SharedAuthenticatedUser will not have any associated share key. + * + * @param authenticatedUser + * The AuthenticatedUser to copy. + */ + public SharedAuthenticatedUser(AuthenticatedUser authenticatedUser) { + super(authenticatedUser.getAuthenticationProvider(), authenticatedUser.getCredentials()); + this.shareKey = null; + this.identifier = authenticatedUser.getIdentifier(); + } + + /** + * Creates a new SharedAuthenticatedUser associating the given user with + * their corresponding credentials and share key. The identifier (username) + * of the user will be randomly generated. + * + * @param authenticationProvider + * The AuthenticationProvider that has authenticated the given user. + * + * @param credentials + * The credentials given by the user when they authenticated. + * + * @param shareKey + * The share key which was provided by this user when they + * authenticated, or null if no share key was provided. + */ + public SharedAuthenticatedUser(AuthenticationProvider authenticationProvider, + Credentials credentials, String shareKey) { + super(authenticationProvider, credentials); + this.shareKey = shareKey; + this.identifier = UUID.randomUUID().toString(); + } + + /** + * Returns the share key which was provided by this user when they + * authenticated. If there is no such share key, null is returned. + * + * @return + * The share key which was provided by this user when they + * authenticated, or null if no share key was provided. + */ + public String getShareKey() { + return shareKey; + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public void setIdentifier(String identifier) { + throw new UnsupportedOperationException("Users authenticated via share keys are immutable."); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java new file mode 100644 index 000000000..2375f0666 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java @@ -0,0 +1,136 @@ +/* + * 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.jdbc.sharing.user; + +import java.util.Collections; +import java.util.Map; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.sharing.permission.SharedObjectPermissionSet; +import org.apache.guacamole.net.auth.AuthenticatedUser; +import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.net.auth.ConnectionGroup; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; +import org.apache.guacamole.net.auth.permission.SystemPermissionSet; +import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet; +import org.apache.guacamole.net.auth.simple.SimpleSystemPermissionSet; + +/** + * An immutable implementation of User which defines READ permission for each of + * the objects accessible through the various directories of a given + * SharedUserContext. + * + * @author Michael Jumper + */ +public class SharedUser implements User { + + /** + * The AuthenticatedUser that this SharedUser represents. + */ + private final AuthenticatedUser user; + + /** + * The SharedUserContext which should be used to define which objects this + * SharedUser has READ permission for. + */ + private final SharedUserContext userContext; + + /** + * Creates a new SharedUser whose identity is defined by the given + * AuthenticatedUser, and who has strictly READ access to all objects + * accessible via the various directories of the given SharedUserContext. + * + * @param user + * The AuthenticatedUser that the SharedUser should represent. + * + * @param userContext + * The SharedUserContext which should be used to define which objects + * the SharedUser has READ permission for. + */ + public SharedUser(AuthenticatedUser user, SharedUserContext userContext) { + this.user = user; + this.userContext = userContext; + } + + @Override + public String getIdentifier() { + return user.getIdentifier(); + } + + @Override + public void setIdentifier(String identifier) { + throw new UnsupportedOperationException("Users authenticated via share keys are immutable."); + } + + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + @Override + public void setAttributes(Map attributes) { + // Do nothing - no attributes supported + } + + @Override + public String getPassword() { + return null; + } + + @Override + public void setPassword(String password) { + throw new UnsupportedOperationException("Users authenticated via share keys are immutable."); + } + + @Override + public SystemPermissionSet getSystemPermissions() throws GuacamoleException { + return new SimpleSystemPermissionSet(); + } + + @Override + public ObjectPermissionSet getConnectionPermissions() throws GuacamoleException { + Directory connectionDirectory = userContext.getConnectionDirectory(); + return new SharedObjectPermissionSet(connectionDirectory.getIdentifiers()); + } + + @Override + public ObjectPermissionSet getConnectionGroupPermissions() throws GuacamoleException { + Directory connectionGroupDirectory = userContext.getConnectionGroupDirectory(); + return new SharedObjectPermissionSet(connectionGroupDirectory.getIdentifiers()); + } + + @Override + public ObjectPermissionSet getUserPermissions() throws GuacamoleException { + Directory userDirectory = userContext.getUserDirectory(); + return new SharedObjectPermissionSet(userDirectory.getIdentifiers()); + } + + @Override + public ObjectPermissionSet getSharingProfilePermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(); + } + + @Override + public ObjectPermissionSet getActiveConnectionPermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUserContext.java similarity index 64% rename from extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUserContext.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUserContext.java index 67d70c6b9..40f2bb5e7 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUserContext.java @@ -17,13 +17,15 @@ * under the License. */ -package org.apache.guacamole.auth.jdbc.sharing; +package org.apache.guacamole.auth.jdbc.sharing.user; import com.google.inject.Inject; -import com.google.inject.Provider; import java.util.Collection; import java.util.Collections; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDirectory; +import org.apache.guacamole.auth.jdbc.sharing.connectiongroup.SharedRootConnectionGroup; +import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser; import org.apache.guacamole.form.Form; import org.apache.guacamole.net.auth.ActiveConnection; import org.apache.guacamole.net.auth.AuthenticationProvider; @@ -34,32 +36,23 @@ import org.apache.guacamole.net.auth.Directory; import org.apache.guacamole.net.auth.SharingProfile; import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.UserContext; -import org.apache.guacamole.net.auth.simple.SimpleConnectionDirectory; -import org.apache.guacamole.net.auth.simple.SimpleConnectionGroup; import org.apache.guacamole.net.auth.simple.SimpleConnectionGroupDirectory; import org.apache.guacamole.net.auth.simple.SimpleConnectionRecordSet; import org.apache.guacamole.net.auth.simple.SimpleDirectory; -import org.apache.guacamole.net.auth.simple.SimpleUser; import org.apache.guacamole.net.auth.simple.SimpleUserDirectory; /** - * The user context of a SharedConnectionUser, providing access ONLY to the - * user themselves, the single SharedConnection associated with that user, and - * an internal root connection group containing only that single - * SharedConnection. + * The user context of a SharedUser, providing access ONLY to the user + * themselves, the any SharedConnections associated with that user via share + * keys, and an internal root connection group containing only those + * connections. * * @author Michael Jumper */ -public class SharedConnectionUserContext implements UserContext { +public class SharedUserContext implements UserContext { /** - * Provider for retrieving SharedConnection instances. - */ - @Inject - private Provider connectionProvider; - - /** - * The AuthenticationProvider that created this SharedConnectionUserContext. + * The AuthenticationProvider that created this SharedUserContext. */ private AuthenticationProvider authProvider; @@ -72,7 +65,8 @@ public class SharedConnectionUserContext implements UserContext { * A directory of all connections visible to the user for whom this user * context was created. */ - private Directory connectionDirectory; + @Inject + private SharedConnectionDirectory connectionDirectory; /** * A directory of all connection groups visible to the user for whom this @@ -94,56 +88,50 @@ public class SharedConnectionUserContext implements UserContext { private ConnectionGroup rootGroup; /** - * Creates a new SharedConnectionUserContext which provides access ONLY to - * the given user, the single SharedConnection associated with that user, - * and an internal root connection group containing only that single - * SharedConnection. + * Creates a new SharedUserContext which provides access ONLY to the given + * user, the SharedConnections associated with the share keys used by that + * user, and an internal root connection group containing only those + * SharedConnections. + * + * @param authProvider + * The AuthenticationProvider that created this + * SharedUserContext; * * @param user - * The SharedConnectionUser for whom this SharedConnectionUserContext - * is being created. + * The RemoteAuthenticatedUser for whom this SharedUserContext is being + * created. */ - public void init(SharedConnectionUser user) { - - // Get the definition of the shared connection - SharedConnectionDefinition definition = - user.getSharedConnectionDefinition(); - - // Create a single shared connection accessible by the user - SharedConnection connection = connectionProvider.get(); - connection.init(user, definition); - - // Build list of all accessible connection identifiers - Collection connectionIdentifiers = - Collections.singletonList(connection.getIdentifier()); + public void init(AuthenticationProvider authProvider, RemoteAuthenticatedUser user) { // Associate the originating authentication provider - this.authProvider = user.getAuthenticationProvider(); + this.authProvider = authProvider; - // The connection directory should contain only the shared connection - this.connectionDirectory = new SimpleConnectionDirectory( - Collections.singletonList(connection)); - - // The user should have access only to the shared connection and himself - this.self = new SimpleUser(user.getIdentifier(), - Collections.singletonList(user.getIdentifier()), - connectionIdentifiers, - Collections.emptyList()); - - // The root group contains only the shared connection - String rootIdentifier = connection.getParentIdentifier(); - this.rootGroup = new SimpleConnectionGroup(rootIdentifier, rootIdentifier, - connectionIdentifiers, Collections.emptyList()); + // Provide access to all connections shared with the given user + this.connectionDirectory.init(user); // The connection group directory contains only the root group + this.rootGroup = new SharedRootConnectionGroup(this); this.connectionGroupDirectory = new SimpleConnectionGroupDirectory( Collections.singletonList(this.rootGroup)); // The user directory contains only this user + this.self = new SharedUser(user, this); this.userDirectory = new SimpleUserDirectory(this.self); } + /** + * Registers a new share key with this SharedUserContext such that the user + * will have access to the connection associated with that share key. The + * share key will be automatically de-registered when it is no longer valid. + * + * @param shareKey + * The share key to register. + */ + public void registerShareKey(String shareKey) { + connectionDirectory.registerShareKey(shareKey); + } + @Override public User self() { return self; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java index 6ec329b3f..e178f7a8e 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java @@ -55,8 +55,7 @@ import org.apache.guacamole.token.StandardTokens; import org.apache.guacamole.token.TokenFilter; import org.mybatis.guice.transactional.Transactional; import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper; -import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionDefinition; -import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUser; +import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterModel; @@ -695,7 +694,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS @Override @Transactional - public GuacamoleTunnel getGuacamoleTunnel(SharedConnectionUser user, + public GuacamoleTunnel getGuacamoleTunnel(RemoteAuthenticatedUser user, SharedConnectionDefinition definition, GuacamoleClientInformation info) throws GuacamoleException { diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java index 5ba171833..c7c7eef2a 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java @@ -24,8 +24,8 @@ import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionDefinition; -import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUser; +import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition; +import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.ConnectionGroup; @@ -173,7 +173,7 @@ public interface GuacamoleTunnelService { * If the connection cannot be established due to concurrent usage * rules. */ - GuacamoleTunnel getGuacamoleTunnel(SharedConnectionUser user, + GuacamoleTunnel getGuacamoleTunnel(RemoteAuthenticatedUser user, SharedConnectionDefinition definition, GuacamoleClientInformation info) throws GuacamoleException;