GUACAMOLE-5: Implement sharing-specific user context and in-memory storage. Add 
additional tracking of connections for sake of sharing.

Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/bfa5c381
Tree: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/bfa5c381
Diff: 
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/bfa5c381

Branch: refs/heads/master
Commit: bfa5c38123390a991dbf29d90516ef5285d63064
Parents: b68a8c1
Author: Michael Jumper <[email protected]>
Authored: Thu Jul 21 13:27:39 2016 -0700
Committer: Michael Jumper <[email protected]>
Committed: Thu Jul 21 13:44:24 2016 -0700

----------------------------------------------------------------------
 .../jdbc/JDBCAuthenticationProviderModule.java  |   2 +
 .../TrackedActiveConnection.java                |  39 +++-
 .../jdbc/sharing/ConnectionSharingService.java  | 162 ++++++++++++++
 .../auth/jdbc/sharing/SharedConnection.java     | 164 ++++++++++++++
 .../auth/jdbc/sharing/SharedConnectionUser.java |  93 ++++++++
 .../sharing/SharedConnectionUserContext.java    | 213 +++++++++++++++++++
 .../user/AuthenticationProviderService.java     |  36 +++-
 7 files changed, 698 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bfa5c381/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.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 0948736..708ec3e 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);

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bfa5c381/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java
----------------------------------------------------------------------
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 d4a23ff..9b39f2f 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;
@@ -38,14 +40,20 @@ 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

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bfa5c381/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java
----------------------------------------------------------------------
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 0000000..8af3ac3
--- /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.<Field>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);
+
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bfa5c381/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnection.java
----------------------------------------------------------------------
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 0000000..984f449
--- /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<String, String> getAttributes() {
+        return Collections.<String, String>emptyMap();
+    }
+
+    @Override
+    public void setAttributes(Map<String, String> attributes) {
+        // Do nothing - no attributes supported
+    }
+
+    @Override
+    public List<? extends ConnectionRecord> getHistory()
+            throws GuacamoleException {
+        return Collections.<ConnectionRecord>emptyList();
+    }
+
+    @Override
+    public Set<String> getSharingProfileIdentifiers()
+            throws GuacamoleException {
+        return Collections.<String>emptySet();
+    }
+
+    @Override
+    public int getActiveConnections() {
+        return 0;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bfa5c381/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnectionUser.java
----------------------------------------------------------------------
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 0000000..125628a
--- /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;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bfa5c381/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/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 0000000..37b344a
--- /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<SharedConnection> 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<Connection> connectionDirectory;
+
+    /**
+     * A directory of all connection groups visible to the user for whom this
+     * user context was created.
+     */
+    private Directory<ConnectionGroup> connectionGroupDirectory;
+
+    /**
+     * A directory of all users visible to the user for whom this user context
+     * was created.
+     */
+    private Directory<User> 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<String> connectionIdentifiers =
+                Collections.singletonList(connection.getIdentifier());
+
+        // The connection directory should contain only the shared connection
+        this.connectionDirectory = new SimpleConnectionDirectory(
+                Collections.<Connection>singletonList(connection));
+
+        // The user should have access only to the shared connection and 
himself
+        this.self = new SimpleUser(user.getIdentifier(),
+                Collections.<String>singletonList(user.getIdentifier()),
+                connectionIdentifiers,
+                Collections.<String>emptyList());
+
+        // The root group contains only the shared connection
+        String rootIdentifier = connection.getParentIdentifier();
+        this.rootGroup = new SimpleConnectionGroup(rootIdentifier, 
rootIdentifier,
+                connectionIdentifiers, Collections.<String>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<User> getUserDirectory() {
+        return userDirectory;
+    }
+
+    @Override
+    public Directory<Connection> getConnectionDirectory()
+            throws GuacamoleException {
+        return connectionDirectory;
+    }
+
+    @Override
+    public Directory<ConnectionGroup> getConnectionGroupDirectory() {
+        return connectionGroupDirectory;
+    }
+
+    @Override
+    public Directory<ActiveConnection> getActiveConnectionDirectory()
+            throws GuacamoleException {
+        return new SimpleDirectory<ActiveConnection>();
+    }
+
+    @Override
+    public Directory<SharingProfile> getSharingProfileDirectory()
+            throws GuacamoleException {
+        return new SimpleDirectory<SharingProfile>();
+    }
+
+    @Override
+    public ConnectionRecordSet getConnectionHistory() {
+        return new SimpleConnectionRecordSet();
+    }
+
+    @Override
+    public ConnectionGroup getRootConnectionGroup() {
+        return rootGroup;
+    }
+
+    @Override
+    public Collection<Form> getUserAttributes() {
+        return Collections.<Form>emptyList();
+    }
+
+    @Override
+    public Collection<Form> getConnectionAttributes() {
+        return Collections.<Form>emptyList();
+    }
+
+    @Override
+    public Collection<Form> getConnectionGroupAttributes() {
+        return Collections.<Form>emptyList();
+    }
+
+    @Override
+    public Collection<Form> getSharingProfileAttributes() {
+        return Collections.<Form>emptyList();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/bfa5c381/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/AuthenticationProviderService.java
----------------------------------------------------------------------
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 2c4484c..3e61cf5 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;
@@ -49,6 +53,18 @@ public class AuthenticationProviderService  {
     private Provider<UserContext> userContextProvider;
 
     /**
+     * Provider for retrieving SharedConnectionUserContext instances.
+     */
+    @Inject
+    private Provider<SharedConnectionUserContext> 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
      * credentials are invalid or expired, an appropriate GuacamoleException
@@ -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);


Reply via email to