From bfa5c38123390a991dbf29d90516ef5285d63064 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 21 Jul 2016 13:27:39 -0700 Subject: [PATCH] GUACAMOLE-5: Implement sharing-specific user context and in-memory storage. Add additional tracking of connections for sake of sharing. --- .../JDBCAuthenticationProviderModule.java | 2 + .../TrackedActiveConnection.java | 39 +++- .../sharing/ConnectionSharingService.java | 162 +++++++++++++ .../auth/jdbc/sharing/SharedConnection.java | 164 ++++++++++++++ .../jdbc/sharing/SharedConnectionUser.java | 93 ++++++++ .../sharing/SharedConnectionUserContext.java | 213 ++++++++++++++++++ .../user/AuthenticationProviderService.java | 36 ++- 7 files changed, 698 insertions(+), 11 deletions(-) create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnection.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUser.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUserContext.java diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java index 0948736b4..708ec3e87 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java @@ -63,6 +63,7 @@ import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper; import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionMapper; import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionService; import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionSet; +import org.apache.guacamole.auth.jdbc.sharing.ConnectionSharingService; import org.apache.guacamole.auth.jdbc.sharing.HashSharedConnectionMap; import org.apache.guacamole.auth.jdbc.sharing.SecureRandomShareKeyGenerator; import org.apache.guacamole.auth.jdbc.sharing.ShareKeyGenerator; @@ -168,6 +169,7 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule { bind(ConnectionGroupPermissionService.class); bind(ConnectionGroupService.class); bind(ConnectionPermissionService.class); + bind(ConnectionSharingService.class); bind(ConnectionService.class); bind(GuacamoleTunnelService.class).to(RestrictedGuacamoleTunnelService.class); bind(PasswordEncryptionService.class).to(SHA256PasswordEncryptionService.class); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java index d4a23ffbc..9b39f2fbc 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java @@ -19,10 +19,12 @@ package org.apache.guacamole.auth.jdbc.activeconnection; +import com.google.inject.Inject; import java.util.Date; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.auth.jdbc.base.RestrictedObject; +import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; +import org.apache.guacamole.auth.jdbc.sharing.ConnectionSharingService; import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord; import org.apache.guacamole.auth.jdbc.user.AuthenticatedUser; import org.apache.guacamole.net.GuacamoleTunnel; @@ -37,15 +39,21 @@ import org.apache.guacamole.net.auth.credentials.UserCredentials; */ public class TrackedActiveConnection extends RestrictedObject implements ActiveConnection { + /** + * Service for managing shared connections. + */ + @Inject + private ConnectionSharingService sharingService; + /** * The identifier of this active connection. */ private String identifier; /** - * The identifier of the associated connection. + * The connection being actively used or shared. */ - private String connectionIdentifier; + private ModeledConnection connection; /** * The identifier of the associated sharing profile. @@ -98,7 +106,7 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC super.init(currentUser); // Copy all non-sensitive data from given record - this.connectionIdentifier = activeConnectionRecord.getConnectionIdentifier(); + this.connection = activeConnectionRecord.getConnection(); this.sharingProfileIdentifier = activeConnectionRecord.getSharingProfileIdentifier(); this.identifier = activeConnectionRecord.getUUID().toString(); this.startDate = activeConnectionRecord.getStartDate(); @@ -121,15 +129,29 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC public void setIdentifier(String identifier) { this.identifier = identifier; } - + + /** + * Returns the connection being actively used. If this active connection is + * not the primary connection, this will be the connection being actively + * shared. + * + * @return + * The connection being actively used. + */ + public ModeledConnection getConnection() { + return connection; + } + @Override public String getConnectionIdentifier() { - return connectionIdentifier; + return connection.getIdentifier(); } @Override public void setConnectionIdentifier(String connnectionIdentifier) { - this.connectionIdentifier = connnectionIdentifier; + throw new UnsupportedOperationException("The connection identifier of " + + "TrackedActiveConnection is inherited from the underlying " + + "connection."); } @Override @@ -145,7 +167,8 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC @Override public UserCredentials getSharingCredentials(String identifier) throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied"); + return sharingService.generateTemporaryCredentials(getCurrentUser(), + this, identifier); } @Override 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 new file mode 100644 index 000000000..8af3ac32a --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java @@ -0,0 +1,162 @@ +/* + * 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 com.google.inject.Inject; +import java.util.Collections; +import javax.servlet.http.HttpServletRequest; +import org.apache.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection; +import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; +import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileService; +import org.apache.guacamole.form.Field; +import org.apache.guacamole.net.auth.AuthenticationProvider; +import org.apache.guacamole.net.auth.Credentials; +import org.apache.guacamole.net.auth.credentials.CredentialsInfo; +import org.apache.guacamole.net.auth.credentials.UserCredentials; + +/** + * Service which provides convenience methods for sharing active connections. + * + * @author Michael Jumper + */ +public class ConnectionSharingService { + + /** + * The name of the query parameter that is used when authenticating obtain + * temporary access to a connection. + */ + public static final String SHARE_KEY_NAME = "key"; + + /** + * Generator for sharing keys. + */ + @Inject + private ShareKeyGenerator keyGenerator; + + /** + * Map of all currently-shared connections. + */ + @Inject + private SharedConnectionMap connectionMap; + + /** + * Service for retrieving and manipulating sharing profile objects. + */ + @Inject + private SharingProfileService sharingProfileService; + + /** + * The credentials expected when a user is authenticating using temporary + * credentials in order to obtain access to a single connection. + */ + public static final CredentialsInfo SHARE_KEY = + new CredentialsInfo(Collections.singletonList( + new Field(SHARE_KEY_NAME, Field.Type.QUERY_PARAMETER) + )); + + /** + * Generates a set of temporary credentials which can be used to connect to + * the given connection using the given sharing profile. If the user does + * not have permission to share the connection via the given sharing + * profile, permission will be denied. + * + * @param user + * The user sharing the connection. + * + * @param activeConnection + * The active connection being shared. + * + * @param sharingProfileIdentifier + * The identifier of the sharing profile dictating the semantics or + * restrictions applying to the shared session. + * + * @return + * A newly-generated set of temporary credentials which can be used to + * connect to the given connection. + * + * @throws GuacamoleException + * If permission to share the given connection is denied. + */ + public UserCredentials generateTemporaryCredentials(AuthenticatedUser user, + TrackedActiveConnection activeConnection, + String sharingProfileIdentifier) throws GuacamoleException { + + // Pull sharing profile (verifying access) + ModeledSharingProfile sharingProfile = + sharingProfileService.retrieveObject(user, + sharingProfileIdentifier); + + // Generate a share key for the requested connection + String key = keyGenerator.getShareKey(); + connectionMap.put(key, new SharedConnectionDefinition(activeConnection, + sharingProfile)); + + // Return credentials defining a single expected parameter + return new UserCredentials(SHARE_KEY, + Collections.singletonMap(SHARE_KEY_NAME, key)); + + } + + /** + * 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. + * + * @param authProvider + * The AuthenticationProvider on behalf of which the user is being + * retrieved. + * + * @param credentials + * 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. + */ + public SharedConnectionUser retrieveSharedConnectionUser( + AuthenticationProvider authProvider, Credentials credentials) { + + // Pull associated HTTP request + HttpServletRequest request = credentials.getRequest(); + if (request == null) + return null; + + // Retrieve the share key from the request + String shareKey = request.getParameter(ConnectionSharingService.SHARE_KEY_NAME); + 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) + return null; + + // Return temporary in-memory user with access only to the shared connection + return new SharedConnectionUser(authProvider, definition, credentials); + + } + +} 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/SharedConnection.java new file mode 100644 index 000000000..984f449f7 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnection.java @@ -0,0 +1,164 @@ +/* + * 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.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.GuacamoleUnsupportedException; +import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection; +import org.apache.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup; +import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; +import org.apache.guacamole.net.GuacamoleTunnel; +import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.net.auth.ConnectionRecord; +import org.apache.guacamole.protocol.GuacamoleClientInformation; +import org.apache.guacamole.protocol.GuacamoleConfiguration; + +/** + * A Connection which joins an active connection, limited by restrictions + * defined by a sharing profile. + * + * @author Michael Jumper + */ +public class SharedConnection implements Connection { + + /** + * 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; + + /** + * The active connection being shared. + */ + private TrackedActiveConnection activeConnection; + + /** + * The sharing profile which dictates the level of access provided to a user + * of the shared connection. + */ + private ModeledSharingProfile sharingProfile; + + /** + * Creates a new SharedConnection which can be used to join the connection + * described by the given SharedConnectionDefinition. + * + * @param user + * The user that successfully authenticated to obtain access to this + * SharedConnection. + * + * @param definition + * The SharedConnectionDefinition dictating the connection being shared + * and any associated restrictions. + */ + public void init(SharedConnectionUser user, SharedConnectionDefinition definition) { + this.user = user; + this.activeConnection = definition.getActiveConnection(); + this.sharingProfile = definition.getSharingProfile(); + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public void setIdentifier(String identifier) { + throw new UnsupportedOperationException("Shared connections are immutable."); + } + + @Override + public String getName() { + return sharingProfile.getName(); + } + + @Override + public void setName(String name) { + throw new UnsupportedOperationException("Shared connections are immutable."); + } + + @Override + public String getParentIdentifier() { + return RootConnectionGroup.IDENTIFIER; + } + + @Override + public void setParentIdentifier(String parentIdentifier) { + throw new UnsupportedOperationException("Shared connections are immutable."); + } + + @Override + public GuacamoleConfiguration getConfiguration() { + GuacamoleConfiguration config = new GuacamoleConfiguration(); + config.setProtocol(activeConnection.getConnection().getConfiguration().getProtocol()); + return config; + } + + @Override + public void setConfiguration(GuacamoleConfiguration config) { + throw new UnsupportedOperationException("Shared connections are immutable."); + } + + @Override + public GuacamoleTunnel connect(GuacamoleClientInformation info) + throws GuacamoleException { + // STUB + throw new GuacamoleUnsupportedException("Connecting to shared connections is not yet implemented."); + } + + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + @Override + public void setAttributes(Map attributes) { + // Do nothing - no attributes supported + } + + @Override + public List getHistory() + throws GuacamoleException { + return Collections.emptyList(); + } + + @Override + public Set getSharingProfileIdentifiers() + throws GuacamoleException { + return Collections.emptySet(); + } + + @Override + public int getActiveConnections() { + return 0; + } + +} 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 new file mode 100644 index 000000000..125628a4c --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUser.java @@ -0,0 +1,93 @@ +/* + * 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/SharedConnectionUserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUserContext.java new file mode 100644 index 000000000..37b344ab3 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUserContext.java @@ -0,0 +1,213 @@ +/* + * 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 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.form.Form; +import org.apache.guacamole.net.auth.ActiveConnection; +import org.apache.guacamole.net.auth.AuthenticationProvider; +import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.net.auth.ConnectionGroup; +import org.apache.guacamole.net.auth.ConnectionRecordSet; +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. + * + * @author Michael Jumper + */ +public class SharedConnectionUserContext implements UserContext { + + /** + * Provider for retrieving SharedConnection instances. + */ + @Inject + private Provider connectionProvider; + + /** + * The AuthenticationProvider that created this SharedConnectionUserContext. + */ + @Inject + private AuthenticationProvider authProvider; + + /** + * The user whose level of access is represented by this user context. + */ + private User self; + + /** + * A directory of all connections visible to the user for whom this user + * context was created. + */ + private Directory connectionDirectory; + + /** + * A directory of all connection groups visible to the user for whom this + * user context was created. + */ + private Directory connectionGroupDirectory; + + /** + * A directory of all users visible to the user for whom this user context + * was created. + */ + private Directory userDirectory; + + /** + * The root connection group of the hierarchy containing all connections + * and connection groups visible to the user for whom this user context was + * created. + */ + 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. + * + * @param user + * The SharedConnectionUser for whom this SharedConnectionUserContext + * 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()); + + // 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()); + + // The connection group directory contains only the root group + this.connectionGroupDirectory = new SimpleConnectionGroupDirectory( + Collections.singletonList(this.rootGroup)); + + // The user directory contains only this user + this.userDirectory = new SimpleUserDirectory(this.self); + + } + + @Override + public User self() { + return self; + } + + @Override + public AuthenticationProvider getAuthenticationProvider() { + return authProvider; + } + + @Override + public Directory getUserDirectory() { + return userDirectory; + } + + @Override + public Directory getConnectionDirectory() + throws GuacamoleException { + return connectionDirectory; + } + + @Override + public Directory getConnectionGroupDirectory() { + return connectionGroupDirectory; + } + + @Override + public Directory getActiveConnectionDirectory() + throws GuacamoleException { + return new SimpleDirectory(); + } + + @Override + public Directory getSharingProfileDirectory() + throws GuacamoleException { + return new SimpleDirectory(); + } + + @Override + public ConnectionRecordSet getConnectionHistory() { + return new SimpleConnectionRecordSet(); + } + + @Override + public ConnectionGroup getRootConnectionGroup() { + return rootGroup; + } + + @Override + public Collection
getUserAttributes() { + return Collections.emptyList(); + } + + @Override + public Collection getConnectionAttributes() { + return Collections.emptyList(); + } + + @Override + public Collection getConnectionGroupAttributes() { + return Collections.emptyList(); + } + + @Override + public Collection getSharingProfileAttributes() { + return Collections.emptyList(); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/AuthenticationProviderService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/AuthenticationProviderService.java index 2c4484c9e..3e61cf536 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/AuthenticationProviderService.java @@ -22,6 +22,10 @@ package org.apache.guacamole.auth.jdbc.user; import com.google.inject.Inject; import com.google.inject.Provider; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.sharing.ConnectionSharingService; +import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUser; +import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUserContext; +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.credentials.CredentialsInfo; @@ -48,6 +52,18 @@ public class AuthenticationProviderService { @Inject private Provider userContextProvider; + /** + * Provider for retrieving SharedConnectionUserContext instances. + */ + @Inject + private Provider sharedUserContextProvider; + + /** + * Service for sharing active connections. + */ + @Inject + private ConnectionSharingService sharingService; + /** * Authenticates the user having the given credentials, returning a new * AuthenticatedUser instance only if the credentials are valid. If the @@ -72,8 +88,15 @@ public class AuthenticationProviderService { public AuthenticatedUser authenticateUser(AuthenticationProvider authenticationProvider, Credentials credentials) throws GuacamoleException { + AuthenticatedUser user; + + // Check whether user is authenticating with a valid sharing key + user = sharingService.retrieveSharedConnectionUser(authenticationProvider, credentials); + if (user != null) + return user; + // Authenticate user - AuthenticatedUser user = userService.retrieveAuthenticatedUser(authenticationProvider, credentials); + user = userService.retrieveAuthenticatedUser(authenticationProvider, credentials); if (user != null) return user; @@ -98,8 +121,15 @@ public class AuthenticationProviderService { * If an error occurs during authentication, or if the given * credentials are invalid or expired. */ - public UserContext getUserContext(org.apache.guacamole.net.auth.AuthenticatedUser authenticatedUser) - throws GuacamoleException { + public org.apache.guacamole.net.auth.UserContext getUserContext( + 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; + } // Retrieve user account for already-authenticated user ModeledUser user = userService.retrieveUser(authenticatedUser);