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

This commit is contained in:
Michael Jumper
2016-07-21 13:27:39 -07:00
parent b68a8c128c
commit bfa5c38123
7 changed files with 698 additions and 11 deletions

View File

@@ -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);

View File

@@ -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();
@@ -122,14 +130,28 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC
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

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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<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
@@ -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);