mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-5: Merge database connection sharing implementation.
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
|
||||
package org.apache.guacamole.auth.jdbc;
|
||||
|
||||
import com.google.inject.Scopes;
|
||||
import org.apache.guacamole.auth.jdbc.user.UserContext;
|
||||
import org.apache.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup;
|
||||
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
|
||||
@@ -62,6 +63,11 @@ 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;
|
||||
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionMap;
|
||||
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
|
||||
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileDirectory;
|
||||
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileMapper;
|
||||
@@ -163,10 +169,13 @@ 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);
|
||||
bind(SaltService.class).to(SecureRandomSaltService.class);
|
||||
bind(SharedConnectionMap.class).to(HashSharedConnectionMap.class).in(Scopes.SINGLETON);
|
||||
bind(ShareKeyGenerator.class).to(SecureRandomShareKeyGenerator.class).in(Scopes.SINGLETON);
|
||||
bind(SharingProfilePermissionService.class);
|
||||
bind(SharingProfileService.class);
|
||||
bind(SystemPermissionService.class);
|
||||
|
@@ -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.
|
||||
@@ -67,6 +75,13 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* The connection ID of the connection as determined by guacd, not to be
|
||||
* confused with the connection identifier determined by the database. This
|
||||
* is the ID that must be supplied to guacd if joining this connection.
|
||||
*/
|
||||
private String connectionID;
|
||||
|
||||
/**
|
||||
* The underlying GuacamoleTunnel.
|
||||
*/
|
||||
@@ -98,7 +113,8 @@ 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.connectionID = activeConnectionRecord.getConnectionID();
|
||||
this.sharingProfileIdentifier = activeConnectionRecord.getSharingProfileIdentifier();
|
||||
this.identifier = activeConnectionRecord.getUUID().toString();
|
||||
this.startDate = activeConnectionRecord.getStartDate();
|
||||
@@ -121,15 +137,42 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection ID of the in-progress connection as determined by
|
||||
* guacd, not to be confused with the connection identifier determined by
|
||||
* the database. This is the ID that must be supplied to guacd if joining
|
||||
* this connection.
|
||||
*
|
||||
* @return
|
||||
* The ID of the in-progress connection, as determined by guacd.
|
||||
*/
|
||||
public String getConnectionID() {
|
||||
return connectionID;
|
||||
}
|
||||
|
||||
@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 +188,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
|
||||
|
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.GuacamoleSecurityException;
|
||||
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);
|
||||
|
||||
// Verify that this profile is indeed a sharing profile for the
|
||||
// requested connection
|
||||
String connectionIdentifier = activeConnection.getConnectionIdentifier();
|
||||
if (sharingProfile == null || !sharingProfile.getPrimaryConnectionIdentifier().equals(connectionIdentifier))
|
||||
throw new GuacamoleSecurityException("Permission denied.");
|
||||
|
||||
// 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* A HashMap-based implementation of the SharedConnectionMap.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class HashSharedConnectionMap implements SharedConnectionMap {
|
||||
|
||||
/**
|
||||
* Keeps track of the share key to SharedConnectionDefinition mapping.
|
||||
*/
|
||||
private final ConcurrentMap<String, SharedConnectionDefinition> connectionMap =
|
||||
new ConcurrentHashMap<String, SharedConnectionDefinition>();
|
||||
|
||||
@Override
|
||||
public SharedConnectionDefinition get(String key) {
|
||||
|
||||
// There are no null share keys
|
||||
if (key == null)
|
||||
return null;
|
||||
|
||||
// Update the last access time and return the SharedConnectionDefinition
|
||||
return connectionMap.get(key);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, SharedConnectionDefinition definition) {
|
||||
connectionMap.put(key, definition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedConnectionDefinition remove(String key) {
|
||||
|
||||
// There are no null share keys
|
||||
if (key == null)
|
||||
return null;
|
||||
|
||||
// Attempt to retrieve only if non-null
|
||||
return connectionMap.remove(key);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.security.SecureRandom;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
/**
|
||||
* An implementation of the ShareKeyGenerator which uses SecureRandom to
|
||||
* generate cryptographically-secure random sharing keys.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class SecureRandomShareKeyGenerator implements ShareKeyGenerator {
|
||||
|
||||
/**
|
||||
* Instance of SecureRandom for generating sharing keys.
|
||||
*/
|
||||
private final SecureRandom secureRandom = new SecureRandom();
|
||||
|
||||
@Override
|
||||
public String getShareKey() {
|
||||
byte[] bytes = new byte[33];
|
||||
secureRandom.nextBytes(bytes);
|
||||
return DatatypeConverter.printBase64Binary(bytes);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Produces unique keys that can be safely used for the automatically-generated
|
||||
* "sharing credentials" associated with a shared connection.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public interface ShareKeyGenerator {
|
||||
|
||||
/**
|
||||
* Returns a new share key, guaranteed to be unique from all previously-
|
||||
* returned share keys.
|
||||
*
|
||||
* @return
|
||||
* The new share key.
|
||||
*/
|
||||
public String getShareKey();
|
||||
|
||||
}
|
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection;
|
||||
import org.apache.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup;
|
||||
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
|
||||
import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
|
||||
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 {
|
||||
|
||||
/**
|
||||
* Service for establishing tunnels to Guacamole connections.
|
||||
*/
|
||||
@Inject
|
||||
private GuacamoleTunnelService tunnelService;
|
||||
|
||||
/**
|
||||
* Randomly-generated unique identifier, guaranteeing this shared connection
|
||||
* does not duplicate the identifying information of the underlying
|
||||
* connection being shared.
|
||||
*/
|
||||
private final String identifier = UUID.randomUUID().toString();
|
||||
|
||||
/**
|
||||
* The user that successfully authenticated to obtain access to this
|
||||
* SharedConnection.
|
||||
*/
|
||||
private SharedConnectionUser user;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
return tunnelService.getGuacamoleTunnel(user, activeConnection,
|
||||
sharingProfile, info);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection;
|
||||
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
|
||||
|
||||
/**
|
||||
* Defines the semantics/restrictions of a shared connection by associating an
|
||||
* active connection with a sharing profile. The sharing profile defines the
|
||||
* access provided to users of the shared active connection through its
|
||||
* connection parameters.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class SharedConnectionDefinition {
|
||||
|
||||
/**
|
||||
* The active connection being shared.
|
||||
*/
|
||||
private final TrackedActiveConnection activeConnection;
|
||||
|
||||
/**
|
||||
* The sharing profile which dictates the level of access provided to a user
|
||||
* of the shared connection.
|
||||
*/
|
||||
private final ModeledSharingProfile sharingProfile;
|
||||
|
||||
/**
|
||||
* Creates a new SharedConnectionDefinition which describes an active
|
||||
* connection that can be joined, including the restrictions dictated by a
|
||||
* given sharing profile.
|
||||
*
|
||||
* @param activeConnection
|
||||
* The active connection being shared.
|
||||
*
|
||||
* @param sharingProfile
|
||||
* A sharing profile whose associated parameters dictate the level of
|
||||
* access provided to the shared connection.
|
||||
*/
|
||||
public SharedConnectionDefinition(TrackedActiveConnection activeConnection,
|
||||
ModeledSharingProfile sharingProfile) {
|
||||
this.activeConnection = activeConnection;
|
||||
this.sharingProfile = sharingProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TrackedActiveConnection of the actual in-progress connection
|
||||
* being shared.
|
||||
*
|
||||
* @return
|
||||
* The TrackedActiveConnection being shared.
|
||||
*/
|
||||
public TrackedActiveConnection getActiveConnection() {
|
||||
return activeConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ModeledSharingProfile whose associated parameters dictate the
|
||||
* level of access granted to users of the shared connection.
|
||||
*
|
||||
* @return
|
||||
* A ModeledSharingProfile whose associated parameters dictate the
|
||||
* level of access granted to users of the shared connection.
|
||||
*/
|
||||
public ModeledSharingProfile getSharingProfile() {
|
||||
return sharingProfile;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents a mapping between share keys and the Guacamole connection being
|
||||
* shared.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public interface SharedConnectionMap {
|
||||
|
||||
/**
|
||||
* Associates the given share key with a SharedConnectionDefinition,
|
||||
* allowing the connection it describes to be accessed by users having the
|
||||
* share key.
|
||||
*
|
||||
* @param key
|
||||
* The share key to use to share the connection described by the given
|
||||
* SharedConnectionDefinition.
|
||||
*
|
||||
* @param definition
|
||||
* The SharedConnectionDefinition describing the connection being
|
||||
* shared via the given share key.
|
||||
*/
|
||||
public void put(String key, SharedConnectionDefinition definition);
|
||||
|
||||
/**
|
||||
* Retrieves the connection definition associated with the given share key.
|
||||
* If no such share key exists, null is returned.
|
||||
*
|
||||
* @param key
|
||||
* The share key associated with the connection definition to be
|
||||
* returned.
|
||||
*
|
||||
* @return
|
||||
* The connection definition associated with the given share key, or
|
||||
* null if no such share key exists.
|
||||
*/
|
||||
public SharedConnectionDefinition get(String key);
|
||||
|
||||
/**
|
||||
* Invalidates given share key, if it exists, returning the connection
|
||||
* definition previously associated with that key. If no such share key
|
||||
* exists, this function has no effect, and null is returned.
|
||||
*
|
||||
* @param key
|
||||
* The share key associated with the connection definition to be
|
||||
* removed.
|
||||
*
|
||||
* @return
|
||||
* The connection definition previously associated with the given
|
||||
* share key, or null if no such share key exists and no connection was
|
||||
* removed.
|
||||
*/
|
||||
public SharedConnectionDefinition remove(String key);
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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();
|
||||
}
|
||||
|
||||
}
|
@@ -38,10 +38,11 @@ import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordMapper;
|
||||
import org.apache.guacamole.auth.jdbc.connection.ConnectionModel;
|
||||
import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel;
|
||||
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterModel;
|
||||
import org.apache.guacamole.auth.jdbc.user.UserModel;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
||||
import org.apache.guacamole.GuacamoleSecurityException;
|
||||
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
|
||||
import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection;
|
||||
import org.apache.guacamole.auth.jdbc.connection.ConnectionMapper;
|
||||
import org.apache.guacamole.environment.Environment;
|
||||
import org.apache.guacamole.net.GuacamoleSocket;
|
||||
@@ -55,6 +56,11 @@ import org.apache.guacamole.token.StandardTokens;
|
||||
import org.apache.guacamole.token.TokenFilter;
|
||||
import org.mybatis.guice.transactional.Transactional;
|
||||
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper;
|
||||
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUser;
|
||||
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
|
||||
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper;
|
||||
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterModel;
|
||||
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
|
||||
|
||||
|
||||
/**
|
||||
@@ -88,7 +94,13 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
* Mapper for accessing connection parameters.
|
||||
*/
|
||||
@Inject
|
||||
private ConnectionParameterMapper parameterMapper;
|
||||
private ConnectionParameterMapper connectionParameterMapper;
|
||||
|
||||
/**
|
||||
* Mapper for accessing sharing profile parameters.
|
||||
*/
|
||||
@Inject
|
||||
private SharingProfileParameterMapper sharingProfileParameterMapper;
|
||||
|
||||
/**
|
||||
* Mapper for accessing connection history.
|
||||
@@ -141,7 +153,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
* @throws GuacamoleException
|
||||
* If access is denied to the given user for any reason.
|
||||
*/
|
||||
protected abstract ModeledConnection acquire(AuthenticatedUser user,
|
||||
protected abstract ModeledConnection acquire(RemoteAuthenticatedUser user,
|
||||
List<ModeledConnection> connections) throws GuacamoleException;
|
||||
|
||||
/**
|
||||
@@ -155,7 +167,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
* @param connection
|
||||
* The connection being released.
|
||||
*/
|
||||
protected abstract void release(AuthenticatedUser user,
|
||||
protected abstract void release(RemoteAuthenticatedUser user,
|
||||
ModeledConnection connection);
|
||||
|
||||
/**
|
||||
@@ -172,7 +184,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
* @throws GuacamoleException
|
||||
* If access is denied to the given user for any reason.
|
||||
*/
|
||||
protected abstract void acquire(AuthenticatedUser user,
|
||||
protected abstract void acquire(RemoteAuthenticatedUser user,
|
||||
ModeledConnectionGroup connectionGroup) throws GuacamoleException;
|
||||
|
||||
/**
|
||||
@@ -186,7 +198,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
* @param connectionGroup
|
||||
* The connection group being released.
|
||||
*/
|
||||
protected abstract void release(AuthenticatedUser user,
|
||||
protected abstract void release(RemoteAuthenticatedUser user,
|
||||
ModeledConnectionGroup connectionGroup);
|
||||
|
||||
/**
|
||||
@@ -206,7 +218,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
* A GuacamoleConfiguration containing the protocol and parameters from
|
||||
* the given connection.
|
||||
*/
|
||||
private GuacamoleConfiguration getGuacamoleConfiguration(AuthenticatedUser user,
|
||||
private GuacamoleConfiguration getGuacamoleConfiguration(RemoteAuthenticatedUser user,
|
||||
ModeledConnection connection) {
|
||||
|
||||
// Generate configuration from available data
|
||||
@@ -217,7 +229,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
config.setProtocol(model.getProtocol());
|
||||
|
||||
// Set parameters from associated data
|
||||
Collection<ConnectionParameterModel> parameters = parameterMapper.select(connection.getIdentifier());
|
||||
Collection<ConnectionParameterModel> parameters = connectionParameterMapper.select(connection.getIdentifier());
|
||||
for (ConnectionParameterModel parameter : parameters)
|
||||
config.setParameter(parameter.getName(), parameter.getValue());
|
||||
|
||||
@@ -232,6 +244,52 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a guacamole configuration which joins the active connection
|
||||
* having the given ID, using the provided sharing profile to restrict the
|
||||
* access provided to the user accessing the shared connection. If tokens
|
||||
* are used in the connection parameter values of the sharing profile,
|
||||
* credentials from the given user will be substituted appropriately.
|
||||
*
|
||||
* @param user
|
||||
* The user whose credentials should be used if necessary.
|
||||
*
|
||||
* @param sharingProfile
|
||||
* The sharing profile whose associated parameters dictate the level
|
||||
* of access granted to the user joining the connection.
|
||||
*
|
||||
* @param connectionID
|
||||
* The ID of the connection being joined, as provided by guacd when the
|
||||
* original connection was established, or null if a new connection
|
||||
* should be created instead.
|
||||
*
|
||||
* @return
|
||||
* A GuacamoleConfiguration containing the protocol and parameters from
|
||||
* the given connection.
|
||||
*/
|
||||
private GuacamoleConfiguration getGuacamoleConfiguration(RemoteAuthenticatedUser user,
|
||||
ModeledSharingProfile sharingProfile, String connectionID) {
|
||||
|
||||
// Generate configuration from available data
|
||||
GuacamoleConfiguration config = new GuacamoleConfiguration();
|
||||
config.setConnectionID(connectionID);
|
||||
|
||||
// Set parameters from associated data
|
||||
Collection<SharingProfileParameterModel> parameters = sharingProfileParameterMapper.select(sharingProfile.getIdentifier());
|
||||
for (SharingProfileParameterModel parameter : parameters)
|
||||
config.setParameter(parameter.getName(), parameter.getValue());
|
||||
|
||||
// Build token filter containing credential tokens
|
||||
TokenFilter tokenFilter = new TokenFilter();
|
||||
StandardTokens.addStandardTokens(tokenFilter, user.getCredentials());
|
||||
|
||||
// Filter the configuration
|
||||
tokenFilter.filterValues(config.getParameters());
|
||||
|
||||
return config;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the given ActiveConnectionRecord to the database. The end date of
|
||||
* the saved record will be populated with the current time.
|
||||
@@ -241,17 +299,15 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
*/
|
||||
private void saveConnectionRecord(ActiveConnectionRecord record) {
|
||||
|
||||
// Get associated connection
|
||||
ModeledConnection connection = record.getConnection();
|
||||
|
||||
// Get associated models
|
||||
AuthenticatedUser user = record.getUser();
|
||||
ConnectionRecordModel recordModel = new ConnectionRecordModel();
|
||||
|
||||
// Copy user information and timestamps into new record
|
||||
recordModel.setUsername(user.getIdentifier());
|
||||
recordModel.setConnectionIdentifier(connection.getIdentifier());
|
||||
recordModel.setConnectionName(connection.getName());
|
||||
recordModel.setUsername(record.getUsername());
|
||||
recordModel.setConnectionIdentifier(record.getConnectionIdentifier());
|
||||
recordModel.setConnectionName(record.getConnectionName());
|
||||
recordModel.setSharingProfileIdentifier(record.getSharingProfileIdentifier());
|
||||
recordModel.setSharingProfileName(record.getSharingProfileName());
|
||||
recordModel.setStartDate(record.getStartDate());
|
||||
recordModel.setEndDate(new Date());
|
||||
|
||||
@@ -329,19 +385,26 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
if (!hasRun.compareAndSet(false, true))
|
||||
return;
|
||||
|
||||
// Get original user and connection
|
||||
AuthenticatedUser user = activeConnection.getUser();
|
||||
ModeledConnection connection = activeConnection.getConnection();
|
||||
|
||||
// Get associated identifiers
|
||||
String identifier = connection.getIdentifier();
|
||||
String parentIdentifier = connection.getParentIdentifier();
|
||||
|
||||
// Release connection
|
||||
// Remove underlying tunnel from list of active tunnels
|
||||
activeTunnels.remove(activeConnection.getUUID().toString());
|
||||
activeConnections.remove(identifier, activeConnection);
|
||||
activeConnectionGroups.remove(parentIdentifier, activeConnection);
|
||||
release(user, connection);
|
||||
|
||||
// Get original user
|
||||
RemoteAuthenticatedUser user = activeConnection.getUser();
|
||||
|
||||
// Release the associated connection if this is the primary connection
|
||||
if (activeConnection.isPrimaryConnection()) {
|
||||
|
||||
// Get connection and associated identifiers
|
||||
ModeledConnection connection = activeConnection.getConnection();
|
||||
String identifier = connection.getIdentifier();
|
||||
String parentIdentifier = connection.getParentIdentifier();
|
||||
|
||||
// Release connection
|
||||
activeConnections.remove(identifier, activeConnection);
|
||||
activeConnectionGroups.remove(parentIdentifier, activeConnection);
|
||||
release(user, connection);
|
||||
|
||||
}
|
||||
|
||||
// Release any associated group
|
||||
if (activeConnection.hasBalancingGroup())
|
||||
@@ -379,25 +442,44 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
* while connection configuration information is being retrieved.
|
||||
*/
|
||||
private GuacamoleTunnel assignGuacamoleTunnel(ActiveConnectionRecord activeConnection,
|
||||
GuacamoleClientInformation info)
|
||||
throws GuacamoleException {
|
||||
GuacamoleClientInformation info) throws GuacamoleException {
|
||||
|
||||
ModeledConnection connection = activeConnection.getConnection();
|
||||
|
||||
// Record new active connection
|
||||
Runnable cleanupTask = new ConnectionCleanupTask(activeConnection);
|
||||
activeTunnels.put(activeConnection.getUUID().toString(), activeConnection);
|
||||
activeConnections.put(connection.getIdentifier(), activeConnection);
|
||||
activeConnectionGroups.put(connection.getParentIdentifier(), activeConnection);
|
||||
|
||||
try {
|
||||
|
||||
GuacamoleConfiguration config;
|
||||
|
||||
// Pull configuration directly from the connection if we are not
|
||||
// joining an active connection
|
||||
if (activeConnection.isPrimaryConnection()) {
|
||||
ModeledConnection connection = activeConnection.getConnection();
|
||||
activeConnections.put(connection.getIdentifier(), activeConnection);
|
||||
activeConnectionGroups.put(connection.getParentIdentifier(), activeConnection);
|
||||
config = getGuacamoleConfiguration(activeConnection.getUser(), connection);
|
||||
}
|
||||
|
||||
// If we ARE joining an active connection, generate a configuration
|
||||
// which does so
|
||||
else {
|
||||
|
||||
// Verify that the connection ID is known
|
||||
String connectionID = activeConnection.getConnectionID();
|
||||
if (!activeConnection.isActive() || connectionID == null)
|
||||
throw new GuacamoleResourceNotFoundException("No existing connection to be joined.");
|
||||
|
||||
// Build configuration from the sharing profile and the ID of
|
||||
// the connection being joined
|
||||
config = getGuacamoleConfiguration(activeConnection.getUser(),
|
||||
activeConnection.getSharingProfile(), connectionID);
|
||||
|
||||
}
|
||||
|
||||
// Obtain socket which will automatically run the cleanup task
|
||||
GuacamoleSocket socket = new ConfiguredGuacamoleSocket(
|
||||
getUnconfiguredGuacamoleSocket(cleanupTask),
|
||||
getGuacamoleConfiguration(activeConnection.getUser(), connection),
|
||||
info
|
||||
);
|
||||
ConfiguredGuacamoleSocket socket = new ConfiguredGuacamoleSocket(
|
||||
getUnconfiguredGuacamoleSocket(cleanupTask), config, info);
|
||||
|
||||
// Assign and return new tunnel
|
||||
return activeConnection.assignGuacamoleTunnel(socket);
|
||||
@@ -596,4 +678,17 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public GuacamoleTunnel getGuacamoleTunnel(SharedConnectionUser user,
|
||||
TrackedActiveConnection activeConnection,
|
||||
ModeledSharingProfile sharingProfile,
|
||||
GuacamoleClientInformation info)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Connect to shared connection
|
||||
return assignGuacamoleTunnel(new ActiveConnectionRecord(user, activeConnection, sharingProfile), info);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -21,20 +21,22 @@ package org.apache.guacamole.auth.jdbc.tunnel;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection;
|
||||
import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
|
||||
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
|
||||
import org.apache.guacamole.auth.jdbc.user.AuthenticatedUser;
|
||||
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
|
||||
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
|
||||
import org.apache.guacamole.net.AbstractGuacamoleTunnel;
|
||||
import org.apache.guacamole.net.GuacamoleSocket;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.guacamole.net.auth.ConnectionRecord;
|
||||
import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket;
|
||||
|
||||
|
||||
/**
|
||||
* A connection record implementation that describes an active connection. As
|
||||
* the associated connection has not yet ended, getEndDate() will always return
|
||||
* null, and isActive() will always return true. The associated start date will
|
||||
* be the time of this objects creation.
|
||||
* null. The associated start date will be the time of this objects creation.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
@@ -44,7 +46,7 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
* The user that connected to the connection associated with this connection
|
||||
* record.
|
||||
*/
|
||||
private final AuthenticatedUser user;
|
||||
private final RemoteAuthenticatedUser user;
|
||||
|
||||
/**
|
||||
* The balancing group from which the associated connection was chosen, if
|
||||
@@ -57,6 +59,13 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
*/
|
||||
private final ModeledConnection connection;
|
||||
|
||||
/**
|
||||
* The sharing profile that was used to access the connection associated
|
||||
* with this connection record. If the connection was accessed directly
|
||||
* (without involving a sharing profile), this will be null.
|
||||
*/
|
||||
private final ModeledSharingProfile sharingProfile;
|
||||
|
||||
/**
|
||||
* The time this connection record was created.
|
||||
*/
|
||||
@@ -66,13 +75,54 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
* The UUID that will be assigned to the underlying tunnel.
|
||||
*/
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
|
||||
/**
|
||||
* The connection ID of the connection as determined by guacd, not to be
|
||||
* confused with the connection identifier determined by the database. This
|
||||
* is the ID that must be supplied to guacd if joining this connection.
|
||||
*/
|
||||
private String connectionID;
|
||||
|
||||
/**
|
||||
* The GuacamoleTunnel used by the connection associated with this
|
||||
* connection record.
|
||||
*/
|
||||
private GuacamoleTunnel tunnel;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new connection record associated with the given user,
|
||||
* connection, balancing connection group, and sharing profile. The given
|
||||
* balancing connection group MUST be the connection group from which the
|
||||
* given connection was chosen, and the given sharing profile MUST be the
|
||||
* sharing profile that was used to share access to the given connection.
|
||||
* The start date of this connection record will be the time of its
|
||||
* creation.
|
||||
*
|
||||
* @param user
|
||||
* The user that connected to the connection associated with this
|
||||
* connection record.
|
||||
*
|
||||
* @param balancingGroup
|
||||
* The balancing group from which the given connection was chosen, or
|
||||
* null if no balancing group is being used.
|
||||
*
|
||||
* @param connection
|
||||
* The connection to associate with this connection record.
|
||||
*
|
||||
* @param sharingProfile
|
||||
* The sharing profile that was used to share access to the given
|
||||
* connection, or null if no sharing profile was used.
|
||||
*/
|
||||
private ActiveConnectionRecord(RemoteAuthenticatedUser user,
|
||||
ModeledConnectionGroup balancingGroup,
|
||||
ModeledConnection connection,
|
||||
ModeledSharingProfile sharingProfile) {
|
||||
this.user = user;
|
||||
this.balancingGroup = balancingGroup;
|
||||
this.connection = connection;
|
||||
this.sharingProfile = sharingProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new connection record associated with the given user,
|
||||
* connection, and balancing connection group. The given balancing
|
||||
@@ -90,12 +140,10 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
* @param connection
|
||||
* The connection to associate with this connection record.
|
||||
*/
|
||||
public ActiveConnectionRecord(AuthenticatedUser user,
|
||||
public ActiveConnectionRecord(RemoteAuthenticatedUser user,
|
||||
ModeledConnectionGroup balancingGroup,
|
||||
ModeledConnection connection) {
|
||||
this.user = user;
|
||||
this.balancingGroup = balancingGroup;
|
||||
this.connection = connection;
|
||||
this(user, balancingGroup, connection, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,11 +158,38 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
* @param connection
|
||||
* The connection to associate with this connection record.
|
||||
*/
|
||||
public ActiveConnectionRecord(AuthenticatedUser user,
|
||||
public ActiveConnectionRecord(RemoteAuthenticatedUser user,
|
||||
ModeledConnection connection) {
|
||||
this(user, null, connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new connection record associated with the given user, active
|
||||
* connection, and sharing profile. The given sharing profile MUST be the
|
||||
* sharing profile that was used to share access to the given connection.
|
||||
* The start date of this connection record will be the time of its
|
||||
* creation.
|
||||
*
|
||||
* @param user
|
||||
* The user that connected to the connection associated with this
|
||||
* connection record.
|
||||
*
|
||||
* @param activeConnection
|
||||
* The active connection which is being shared to the given user via
|
||||
* the given sharing profile.
|
||||
*
|
||||
* @param sharingProfile
|
||||
* The sharing profile that was used to share access to the given
|
||||
* connection. As a record created in this way always refers to a
|
||||
* shared connection, this value may NOT be null.
|
||||
*/
|
||||
public ActiveConnectionRecord(RemoteAuthenticatedUser user,
|
||||
TrackedActiveConnection activeConnection,
|
||||
ModeledSharingProfile sharingProfile) {
|
||||
this(user, null, activeConnection.getConnection(), sharingProfile);
|
||||
this.connectionID = activeConnection.getConnectionID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user that connected to the connection associated with this
|
||||
* connection record.
|
||||
@@ -123,7 +198,7 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
* The user that connected to the connection associated with this
|
||||
* connection record.
|
||||
*/
|
||||
public AuthenticatedUser getUser() {
|
||||
public RemoteAuthenticatedUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -149,6 +224,20 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sharing profile that was used to access the connection
|
||||
* associated with this connection record. If the connection was accessed
|
||||
* directly (without involving a sharing profile), this will be null.
|
||||
*
|
||||
* @return
|
||||
* The sharing profile that was used to access the connection
|
||||
* associated with this connection record, or null if the connection
|
||||
* was accessed directly.
|
||||
*/
|
||||
public ModeledSharingProfile getSharingProfile() {
|
||||
return sharingProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the connection associated with this connection record
|
||||
* was chosen from a balancing group.
|
||||
@@ -161,6 +250,21 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
return balancingGroup != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this connection record is associated with a connection
|
||||
* being used directly, in the absence of a sharing profile. If a connection
|
||||
* is shared, this will continue to return false for the connection being
|
||||
* shared, but will return true for the connections which join that
|
||||
* connection.
|
||||
*
|
||||
* @return
|
||||
* true if the connection associated with this connection record is
|
||||
* being used directly, false otherwise.
|
||||
*/
|
||||
public boolean isPrimaryConnection() {
|
||||
return sharingProfile == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConnectionIdentifier() {
|
||||
return connection.getIdentifier();
|
||||
@@ -173,12 +277,26 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
|
||||
@Override
|
||||
public String getSharingProfileIdentifier() {
|
||||
|
||||
// Return sharing profile identifier if known
|
||||
if (sharingProfile != null)
|
||||
return sharingProfile.getIdentifier();
|
||||
|
||||
// No associated sharing profile
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSharingProfileName() {
|
||||
|
||||
// Return sharing profile name if known
|
||||
if (sharingProfile != null)
|
||||
return sharingProfile.getName();
|
||||
|
||||
// No associated sharing profile
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -201,15 +319,12 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return user.getUser().getIdentifier();
|
||||
return user.getIdentifier();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
|
||||
// Active connections are active by definition
|
||||
return true;
|
||||
|
||||
return tunnel != null && tunnel.isOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,13 +344,13 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
* given socket.
|
||||
*
|
||||
* @param socket
|
||||
* The GuacamoleSocket to use to create the tunnel associated with this
|
||||
* connection record.
|
||||
* The ConfiguredGuacamoleSocket to use to create the tunnel associated
|
||||
* with this connection record.
|
||||
*
|
||||
* @return
|
||||
* The newly-created tunnel associated with this connection record.
|
||||
*/
|
||||
public GuacamoleTunnel assignGuacamoleTunnel(final GuacamoleSocket socket) {
|
||||
public GuacamoleTunnel assignGuacamoleTunnel(final ConfiguredGuacamoleSocket socket) {
|
||||
|
||||
// Create tunnel with given socket
|
||||
this.tunnel = new AbstractGuacamoleTunnel() {
|
||||
@@ -252,6 +367,10 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
|
||||
};
|
||||
|
||||
// Store connection ID of the primary connection only
|
||||
if (isPrimaryConnection())
|
||||
this.connectionID = socket.getConnectionID();
|
||||
|
||||
// Return newly-created tunnel
|
||||
return this.tunnel;
|
||||
|
||||
@@ -268,5 +387,20 @@ public class ActiveConnectionRecord implements ConnectionRecord {
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the connection ID of the in-progress connection as determined by
|
||||
* guacd, not to be confused with the connection identifier determined by
|
||||
* the database. This is the ID that must be supplied to guacd if joining
|
||||
* this connection. If the in-progress connection is joining another
|
||||
* connection, this will be the ID of the connection being joined, NOT the
|
||||
* ID of the connection directly represented by this record.
|
||||
*
|
||||
* @return
|
||||
* The ID of the in-progress connection, as determined by guacd.
|
||||
*/
|
||||
public String getConnectionID() {
|
||||
return connectionID;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -24,6 +24,9 @@ import org.apache.guacamole.auth.jdbc.user.AuthenticatedUser;
|
||||
import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
|
||||
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection;
|
||||
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUser;
|
||||
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.ConnectionGroup;
|
||||
@@ -145,4 +148,39 @@ public interface GuacamoleTunnelService {
|
||||
*/
|
||||
public Collection<ActiveConnectionRecord> getActiveConnections(ConnectionGroup connectionGroup);
|
||||
|
||||
/**
|
||||
* Creates a socket for the given user which joins the given active
|
||||
* connection. The given client information will be passed to guacd when
|
||||
* the connection is established. This function will apply any concurrent
|
||||
* usage rules in effect, but will NOT test object- or system-level
|
||||
* permissions.
|
||||
*
|
||||
* @param user
|
||||
* The user for whom the connection is being established.
|
||||
*
|
||||
* @param activeConnection
|
||||
* The active connection the user is joining.
|
||||
*
|
||||
* @param sharingProfile
|
||||
* The sharing profile whose associated parameters dictate the level
|
||||
* of access granted to the user joining the connection.
|
||||
*
|
||||
* @param info
|
||||
* Information describing the Guacamole client connecting to the given
|
||||
* connection.
|
||||
*
|
||||
* @return
|
||||
* A new GuacamoleTunnel which is configured and connected to the given
|
||||
* active connection.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the connection cannot be established due to concurrent usage
|
||||
* rules.
|
||||
*/
|
||||
GuacamoleTunnel getGuacamoleTunnel(SharedConnectionUser user,
|
||||
TrackedActiveConnection activeConnection,
|
||||
ModeledSharingProfile sharingProfile,
|
||||
GuacamoleClientInformation info)
|
||||
throws GuacamoleException;
|
||||
|
||||
}
|
||||
|
@@ -27,12 +27,12 @@ import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.apache.guacamole.GuacamoleClientTooManyException;
|
||||
import org.apache.guacamole.auth.jdbc.user.AuthenticatedUser;
|
||||
import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleResourceConflictException;
|
||||
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
|
||||
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
|
||||
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
|
||||
|
||||
|
||||
/**
|
||||
@@ -166,7 +166,7 @@ public class RestrictedGuacamoleTunnelService
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModeledConnection acquire(AuthenticatedUser user,
|
||||
protected ModeledConnection acquire(RemoteAuthenticatedUser user,
|
||||
List<ModeledConnection> connections) throws GuacamoleException {
|
||||
|
||||
// Do not acquire connection unless within overall limits
|
||||
@@ -174,7 +174,7 @@ public class RestrictedGuacamoleTunnelService
|
||||
throw new GuacamoleResourceConflictException("Cannot connect. Overall maximum connections reached.");
|
||||
|
||||
// Get username
|
||||
String username = user.getUser().getIdentifier();
|
||||
String username = user.getIdentifier();
|
||||
|
||||
// Sort connections in ascending order of usage
|
||||
ModeledConnection[] sortedConnections = connections.toArray(new ModeledConnection[connections.size()]);
|
||||
@@ -230,18 +230,18 @@ public class RestrictedGuacamoleTunnelService
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void release(AuthenticatedUser user, ModeledConnection connection) {
|
||||
activeSeats.remove(new Seat(user.getUser().getIdentifier(), connection.getIdentifier()));
|
||||
protected void release(RemoteAuthenticatedUser user, ModeledConnection connection) {
|
||||
activeSeats.remove(new Seat(user.getIdentifier(), connection.getIdentifier()));
|
||||
activeConnections.remove(connection.getIdentifier());
|
||||
totalActiveConnections.decrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void acquire(AuthenticatedUser user,
|
||||
protected void acquire(RemoteAuthenticatedUser user,
|
||||
ModeledConnectionGroup connectionGroup) throws GuacamoleException {
|
||||
|
||||
// Get username
|
||||
String username = user.getUser().getIdentifier();
|
||||
String username = user.getIdentifier();
|
||||
|
||||
// Attempt to aquire connection group according to per-user limits
|
||||
Seat seat = new Seat(username, connectionGroup.getIdentifier());
|
||||
@@ -267,9 +267,9 @@ public class RestrictedGuacamoleTunnelService
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void release(AuthenticatedUser user,
|
||||
protected void release(RemoteAuthenticatedUser user,
|
||||
ModeledConnectionGroup connectionGroup) {
|
||||
activeGroupSeats.remove(new Seat(user.getUser().getIdentifier(), connectionGroup.getIdentifier()));
|
||||
activeGroupSeats.remove(new Seat(user.getIdentifier(), connectionGroup.getIdentifier()));
|
||||
activeGroups.remove(connectionGroup.getIdentifier());
|
||||
}
|
||||
|
||||
|
@@ -22,9 +22,6 @@ package org.apache.guacamole.auth.jdbc.user;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.guacamole.net.auth.AuthenticationProvider;
|
||||
import org.apache.guacamole.net.auth.Credentials;
|
||||
|
||||
@@ -33,49 +30,13 @@ import org.apache.guacamole.net.auth.Credentials;
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class AuthenticatedUser implements org.apache.guacamole.net.auth.AuthenticatedUser {
|
||||
public class AuthenticatedUser extends RemoteAuthenticatedUser {
|
||||
|
||||
/**
|
||||
* The user that authenticated.
|
||||
*/
|
||||
private final ModeledUser user;
|
||||
|
||||
/**
|
||||
* The credentials given when this user authenticated.
|
||||
*/
|
||||
private final Credentials credentials;
|
||||
|
||||
/**
|
||||
* The AuthenticationProvider that authenticated this user.
|
||||
*/
|
||||
private final AuthenticationProvider authenticationProvider;
|
||||
|
||||
/**
|
||||
* The host from which this user authenticated.
|
||||
*/
|
||||
private final String remoteHost;
|
||||
|
||||
/**
|
||||
* Regular expression which matches any IPv4 address.
|
||||
*/
|
||||
private static final String IPV4_ADDRESS_REGEX = "([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})";
|
||||
|
||||
/**
|
||||
* Regular expression which matches any IPv6 address.
|
||||
*/
|
||||
private static final String IPV6_ADDRESS_REGEX = "([0-9a-fA-F]*(:[0-9a-fA-F]*){0,7})";
|
||||
|
||||
/**
|
||||
* Regular expression which matches any IP address, regardless of version.
|
||||
*/
|
||||
private static final String IP_ADDRESS_REGEX = "(" + IPV4_ADDRESS_REGEX + "|" + IPV6_ADDRESS_REGEX + ")";
|
||||
|
||||
/**
|
||||
* Pattern which matches valid values of the de-facto standard
|
||||
* "X-Forwarded-For" header.
|
||||
*/
|
||||
private static final Pattern X_FORWARDED_FOR = Pattern.compile("^" + IP_ADDRESS_REGEX + "(, " + IP_ADDRESS_REGEX + ")*$");
|
||||
|
||||
/**
|
||||
* The connections which have been committed for use by this user in the
|
||||
* context of a balancing connection group. Balancing connection groups
|
||||
@@ -87,38 +48,6 @@ public class AuthenticatedUser implements org.apache.guacamole.net.auth.Authenti
|
||||
private final Set<String> preferredConnections =
|
||||
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
|
||||
|
||||
/**
|
||||
* Derives the remote host of the authenticating user from the given
|
||||
* credentials object. The remote host is derived from X-Forwarded-For
|
||||
* in addition to the actual source IP of the request, and thus is not
|
||||
* trusted. The derived remote host is really only useful for logging,
|
||||
* unless the server is configured such that X-Forwarded-For is guaranteed
|
||||
* to be trustworthy.
|
||||
*
|
||||
* @param credentials
|
||||
* The credentials to derive the remote host from.
|
||||
*
|
||||
* @return
|
||||
* The remote host from which the user with the given credentials is
|
||||
* authenticating.
|
||||
*/
|
||||
private static String getRemoteHost(Credentials credentials) {
|
||||
|
||||
HttpServletRequest request = credentials.getRequest();
|
||||
|
||||
// Use X-Forwarded-For, if present and valid
|
||||
String header = request.getHeader("X-Forwarded-For");
|
||||
if (header != null) {
|
||||
Matcher matcher = X_FORWARDED_FOR.matcher(header);
|
||||
if (matcher.matches())
|
||||
return matcher.group(1);
|
||||
}
|
||||
|
||||
// If header absent or invalid, just use source IP
|
||||
return request.getRemoteAddr();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new AuthenticatedUser associating the given user with their
|
||||
* corresponding credentials.
|
||||
@@ -134,10 +63,8 @@ public class AuthenticatedUser implements org.apache.guacamole.net.auth.Authenti
|
||||
*/
|
||||
public AuthenticatedUser(AuthenticationProvider authenticationProvider,
|
||||
ModeledUser user, Credentials credentials) {
|
||||
this.authenticationProvider = authenticationProvider;
|
||||
super(authenticationProvider, credentials);
|
||||
this.user = user;
|
||||
this.credentials = credentials;
|
||||
this.remoteHost = getRemoteHost(credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,27 +77,6 @@ public class AuthenticatedUser implements org.apache.guacamole.net.auth.Authenti
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the credentials given during authentication by this user.
|
||||
*
|
||||
* @return
|
||||
* The credentials given during authentication by this user.
|
||||
*/
|
||||
@Override
|
||||
public Credentials getCredentials() {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host from which this user authenticated.
|
||||
*
|
||||
* @return
|
||||
* The host from which this user authenticated.
|
||||
*/
|
||||
public String getRemoteHost() {
|
||||
return remoteHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the connection having the given identifier has been
|
||||
* marked as preferred for this user's current Guacamole session. A
|
||||
@@ -201,11 +107,6 @@ public class AuthenticatedUser implements org.apache.guacamole.net.auth.Authenti
|
||||
preferredConnections.add(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationProvider getAuthenticationProvider() {
|
||||
return authenticationProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return user.getIdentifier();
|
||||
|
@@ -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);
|
||||
|
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.user;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
||||
import org.apache.guacamole.net.auth.AuthenticationProvider;
|
||||
import org.apache.guacamole.net.auth.Credentials;
|
||||
|
||||
/**
|
||||
* An AuthenticatedUser that has an associated remote host.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public abstract class RemoteAuthenticatedUser implements AuthenticatedUser {
|
||||
|
||||
/**
|
||||
* The credentials given when this user authenticated.
|
||||
*/
|
||||
private final Credentials credentials;
|
||||
|
||||
/**
|
||||
* The AuthenticationProvider that authenticated this user.
|
||||
*/
|
||||
private final AuthenticationProvider authenticationProvider;
|
||||
|
||||
/**
|
||||
* The host from which this user authenticated.
|
||||
*/
|
||||
private final String remoteHost;
|
||||
|
||||
/**
|
||||
* Regular expression which matches any IPv4 address.
|
||||
*/
|
||||
private static final String IPV4_ADDRESS_REGEX = "([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})";
|
||||
|
||||
/**
|
||||
* Regular expression which matches any IPv6 address.
|
||||
*/
|
||||
private static final String IPV6_ADDRESS_REGEX = "([0-9a-fA-F]*(:[0-9a-fA-F]*){0,7})";
|
||||
|
||||
/**
|
||||
* Regular expression which matches any IP address, regardless of version.
|
||||
*/
|
||||
private static final String IP_ADDRESS_REGEX = "(" + IPV4_ADDRESS_REGEX + "|" + IPV6_ADDRESS_REGEX + ")";
|
||||
|
||||
/**
|
||||
* Pattern which matches valid values of the de-facto standard
|
||||
* "X-Forwarded-For" header.
|
||||
*/
|
||||
private static final Pattern X_FORWARDED_FOR = Pattern.compile("^" + IP_ADDRESS_REGEX + "(, " + IP_ADDRESS_REGEX + ")*$");
|
||||
|
||||
/**
|
||||
* Derives the remote host of the authenticating user from the given
|
||||
* credentials object. The remote host is derived from X-Forwarded-For
|
||||
* in addition to the actual source IP of the request, and thus is not
|
||||
* trusted. The derived remote host is really only useful for logging,
|
||||
* unless the server is configured such that X-Forwarded-For is guaranteed
|
||||
* to be trustworthy.
|
||||
*
|
||||
* @param credentials
|
||||
* The credentials to derive the remote host from.
|
||||
*
|
||||
* @return
|
||||
* The remote host from which the user with the given credentials is
|
||||
* authenticating.
|
||||
*/
|
||||
private static String getRemoteHost(Credentials credentials) {
|
||||
|
||||
HttpServletRequest request = credentials.getRequest();
|
||||
|
||||
// Use X-Forwarded-For, if present and valid
|
||||
String header = request.getHeader("X-Forwarded-For");
|
||||
if (header != null) {
|
||||
Matcher matcher = X_FORWARDED_FOR.matcher(header);
|
||||
if (matcher.matches())
|
||||
return matcher.group(1);
|
||||
}
|
||||
|
||||
// If header absent or invalid, just use source IP
|
||||
return request.getRemoteAddr();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new RemoteAuthenticatedUser, deriving the associated remote
|
||||
* host from the given credentials.
|
||||
*
|
||||
* @param authenticationProvider
|
||||
* The AuthenticationProvider that has authenticated the given user.
|
||||
*
|
||||
* @param credentials
|
||||
* The credentials given by the user when they authenticated.
|
||||
*/
|
||||
public RemoteAuthenticatedUser(AuthenticationProvider authenticationProvider,
|
||||
Credentials credentials) {
|
||||
this.authenticationProvider = authenticationProvider;
|
||||
this.credentials = credentials;
|
||||
this.remoteHost = getRemoteHost(credentials);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Credentials getCredentials() {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host from which this user authenticated.
|
||||
*
|
||||
* @return
|
||||
* The host from which this user authenticated.
|
||||
*/
|
||||
public String getRemoteHost() {
|
||||
return remoteHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationProvider getAuthenticationProvider() {
|
||||
return authenticationProvider;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user