GUACAMOLE-5: Implement exposure of a dynamic set of share keys as connections within a connection directory.

This commit is contained in:
Michael Jumper
2016-07-29 14:36:11 -07:00
parent 29f9aea1e8
commit 96094a1c58
15 changed files with 712 additions and 194 deletions

View File

@@ -25,6 +25,8 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
import org.apache.guacamole.auth.jdbc.sharing.user.SharedAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileService; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileService;
import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord; import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord;
@@ -128,11 +130,11 @@ public class ConnectionSharingService {
} }
/** /**
* Returns a SharedConnectionUser (an implementation of AuthenticatedUser) * Returns a SharedAuthenticatedUser if the given credentials contain a
* if the given credentials contain a valid share key. The returned user * valid share key. The returned user will be associated with the single
* will be associated with the single shared connection to which they have * shared connection to which they have been granted temporary access. If
* been granted temporary access. If the share key is invalid, or no share * the share key is invalid, or no share key is contained within the given
* key is contained within the given credentials, null is returned. * credentials, null is returned.
* *
* @param authProvider * @param authProvider
* The AuthenticationProvider on behalf of which the user is being * The AuthenticationProvider on behalf of which the user is being
@@ -142,11 +144,11 @@ public class ConnectionSharingService {
* The credentials which are expected to contain the share key. * The credentials which are expected to contain the share key.
* *
* @return * @return
* A SharedConnectionUser with access to a single shared connection, if * A SharedAuthenticatedUser with access to a single shared connection,
* the share key within the given credentials is valid, or null if the * if the share key within the given credentials is valid, or null if
* share key is invalid or absent. * the share key is invalid or absent.
*/ */
public SharedConnectionUser retrieveSharedConnectionUser( public SharedAuthenticatedUser retrieveSharedConnectionUser(
AuthenticationProvider authProvider, Credentials credentials) { AuthenticationProvider authProvider, Credentials credentials) {
// Pull associated HTTP request // Pull associated HTTP request
@@ -159,14 +161,12 @@ public class ConnectionSharingService {
if (shareKey == null) if (shareKey == null)
return null; return null;
// Pull the connection definition describing the connection these // Validate the share key
// credentials provide access to (if any) if (connectionMap.get(shareKey) == null)
SharedConnectionDefinition definition = connectionMap.get(shareKey);
if (definition == null)
return null; return null;
// Return temporary in-memory user with access only to the shared connection // Return temporary in-memory user
return new SharedConnectionUser(authProvider, definition, credentials); return new SharedAuthenticatedUser(authProvider, credentials, shareKey);
} }

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.jdbc.sharing;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
/** /**
* A HashMap-based implementation of the SharedConnectionMap. * A HashMap-based implementation of the SharedConnectionMap.

View File

@@ -23,6 +23,8 @@ import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.AuthenticationProviderService; import org.apache.guacamole.auth.jdbc.AuthenticationProviderService;
import org.apache.guacamole.auth.jdbc.sharing.user.SharedAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.sharing.user.SharedUserContext;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
@@ -31,8 +33,8 @@ import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsExce
/** /**
* Service which authenticates users based on share keys and provides for the * Service which authenticates users based on share keys and provides for the
* creation of corresponding. The created UserContext objects are restricted to * creation of corresponding UserContexts. The created UserContext objects are
* the connections associated with those share keys via a common * restricted to the connections associated with those share keys via a common
* ConnectionSharingService. * ConnectionSharingService.
* *
* @author Michael Jumper * @author Michael Jumper
@@ -40,10 +42,10 @@ import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsExce
public class SharedAuthenticationProviderService implements AuthenticationProviderService { public class SharedAuthenticationProviderService implements AuthenticationProviderService {
/** /**
* Provider for retrieving SharedConnectionUserContext instances. * Provider for retrieving SharedUserContext instances.
*/ */
@Inject @Inject
private Provider<SharedConnectionUserContext> sharedUserContextProvider; private Provider<SharedUserContext> sharedUserContextProvider;
/** /**
* Service for sharing active connections. * Service for sharing active connections.
@@ -66,19 +68,29 @@ public class SharedAuthenticationProviderService implements AuthenticationProvid
} }
@Override @Override
public org.apache.guacamole.net.auth.UserContext getUserContext( public SharedUserContext getUserContext(
AuthenticationProvider authenticationProvider, AuthenticationProvider authenticationProvider,
AuthenticatedUser authenticatedUser) throws GuacamoleException { AuthenticatedUser authenticatedUser) throws GuacamoleException {
// Produce sharing-specific user context if this is the user of a shared connection // Obtain a reference to a correct AuthenticatedUser which can be used
if (authenticatedUser instanceof SharedConnectionUser) { // for accessing shared connections
SharedConnectionUserContext context = sharedUserContextProvider.get(); SharedAuthenticatedUser sharedAuthenticatedUser;
context.init((SharedConnectionUser) authenticatedUser); if (authenticatedUser instanceof SharedAuthenticatedUser)
return context; sharedAuthenticatedUser = (SharedAuthenticatedUser) authenticatedUser;
} else
sharedAuthenticatedUser = new SharedAuthenticatedUser(authenticatedUser);
// No shared connections otherwise // Produce empty user context for known-authenticated user
return null; SharedUserContext context = sharedUserContextProvider.get();
context.init(authenticationProvider, sharedAuthenticatedUser);
// Add the shared connection associated with the originally-provided
// share key (if any)
String shareKey = sharedAuthenticatedUser.getShareKey();
if (shareKey != null)
context.registerShareKey(shareKey);
return context;
} }

View File

@@ -19,6 +19,8 @@
package org.apache.guacamole.auth.jdbc.sharing; package org.apache.guacamole.auth.jdbc.sharing;
import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
/** /**
* Represents a mapping between share keys and the Guacamole connection being * Represents a mapping between share keys and the Guacamole connection being
* shared. * shared.

View File

@@ -1,93 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.jdbc.sharing;
import java.util.UUID;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
/**
* A temporary user who has authenticated using a share key and thus has
* restricted access to a single shared connection.
*
* @author Michael Jumper
*/
public class SharedConnectionUser extends RemoteAuthenticatedUser {
/**
* The single shared connection to which this user has access.
*/
private final SharedConnectionDefinition definition;
/**
* An arbitrary identifier guaranteed to be unique across users. Note that
* because Guacamole users the AuthenticatedUser's identifier as the means
* of determining overall user identity and aggregating data across
* multiple extensions, this identifier MUST NOT match the identifier of
* any possibly existing user (or else the user may unexpectedly gain
* access to another identically-named user's data).
*/
private final String identifier = UUID.randomUUID().toString();
/**
* Creates a new SharedConnectionUser with access solely to connection
* described by the given SharedConnectionDefinition.
*
* @param authenticationProvider
* The AuthenticationProvider that has authenticated the given user.
*
* @param definition
* The SharedConnectionDefinition describing the connection that this
* user should have access to, along with any associated restrictions.
*
* @param credentials
* The credentials given by the user when they authenticated.
*/
public SharedConnectionUser(AuthenticationProvider authenticationProvider,
SharedConnectionDefinition definition, Credentials credentials) {
super(authenticationProvider, credentials);
this.definition = definition;
}
@Override
public String getIdentifier() {
return identifier;
}
@Override
public void setIdentifier(String identifier) {
throw new UnsupportedOperationException("Shared connection users are immutable");
}
/**
* Returns the SharedConnectionDefinition which describes the connection
* that this user should have access to, along with any associated
* restrictions.
*
* @return
* The SharedConnectionDefinition describing the connection that this
* user should have access to, along with any associated restrictions.
*/
public SharedConnectionDefinition getSharedConnectionDefinition() {
return definition;
}
}

View File

@@ -17,17 +17,17 @@
* under the License. * under the License.
*/ */
package org.apache.guacamole.auth.jdbc.sharing; package org.apache.guacamole.auth.jdbc.sharing.connection;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup; import org.apache.guacamole.auth.jdbc.sharing.connectiongroup.SharedRootConnectionGroup;
import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionRecord; import org.apache.guacamole.net.auth.ConnectionRecord;
@@ -48,18 +48,11 @@ public class SharedConnection implements Connection {
@Inject @Inject
private GuacamoleTunnelService tunnelService; 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 * The user that successfully authenticated to obtain access to this
* SharedConnection. * SharedConnection.
*/ */
private SharedConnectionUser user; private RemoteAuthenticatedUser user;
/** /**
* The SharedConnectionDefinition dictating the connection being shared and * The SharedConnectionDefinition dictating the connection being shared and
@@ -79,14 +72,14 @@ public class SharedConnection implements Connection {
* The SharedConnectionDefinition dictating the connection being shared * The SharedConnectionDefinition dictating the connection being shared
* and any associated restrictions. * and any associated restrictions.
*/ */
public void init(SharedConnectionUser user, SharedConnectionDefinition definition) { public void init(RemoteAuthenticatedUser user, SharedConnectionDefinition definition) {
this.user = user; this.user = user;
this.definition = definition; this.definition = definition;
} }
@Override @Override
public String getIdentifier() { public String getIdentifier() {
return identifier; return definition.getShareKey();
} }
@Override @Override
@@ -106,7 +99,7 @@ public class SharedConnection implements Connection {
@Override @Override
public String getParentIdentifier() { public String getParentIdentifier() {
return RootConnectionGroup.IDENTIFIER; return SharedRootConnectionGroup.IDENTIFIER;
} }
@Override @Override

View File

@@ -17,9 +17,10 @@
* under the License. * under the License.
*/ */
package org.apache.guacamole.auth.jdbc.sharing; package org.apache.guacamole.auth.jdbc.sharing.connection;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.sharing.SharedObjectManager;
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord; import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;

View File

@@ -0,0 +1,158 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.jdbc.sharing.connection;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionMap;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.Directory;
/**
* A Directory implementation which exposes an explicitly-registered set of
* share keys as connections. Only explicitly-registered share keys are
* accessible a SharedConnectionDirectory.
*
* @author Michael Jumper
*/
public class SharedConnectionDirectory implements Directory<Connection> {
/**
* Map of all currently-shared connections.
*/
@Inject
private SharedConnectionMap connectionMap;
/**
* Provider for retrieving SharedConnection instances.
*/
@Inject
private Provider<SharedConnection> connectionProvider;
/**
* The user associated with the UserContext that contains this directory.
*/
private RemoteAuthenticatedUser currentUser;
/**
* The set of share keys that have been explicitly registered. In general,
* only valid share keys will be present here, but invalid keys are only
* removed after an attempt to retrieve those keys has been made.
*/
private final Set<String> shareKeys =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
/**
* Creates a new SharedConnectionDirectory which exposes share keys as
* connections. Only explicitly-registered and valid share keys will be
* accessible.
*
* @param currentUser
* The user associated with the UserContext that will contain this
* directory.
*/
public void init(RemoteAuthenticatedUser currentUser) {
this.currentUser = currentUser;
}
/**
* Registers a new share key such that the connection associated with that
* share key is accessible through this directory. The share key will be
* automatically de-registered when it is no longer valid.
*
* @param shareKey
* The share key to register.
*/
public void registerShareKey(String shareKey) {
shareKeys.add(shareKey);
}
@Override
public Connection get(String identifier) throws GuacamoleException {
// Allow access only to registered share keys
if (!shareKeys.contains(identifier))
return null;
// Retrieve the connection definition associated with that share key,
// cleaning up the internally-stored share key if it's no longer valid
SharedConnectionDefinition connectionDefinition = connectionMap.get(identifier);
if (connectionDefinition == null) {
shareKeys.remove(identifier);
return null;
}
// Return a Connection which wraps that connection definition
SharedConnection connection = connectionProvider.get();
connection.init(currentUser, connectionDefinition);
return connection;
}
@Override
public Collection<Connection> getAll(Collection<String> identifiers)
throws GuacamoleException {
// Create collection with enough backing space to contain one
// connection per identifier given
Collection<Connection> matchingConnections =
new ArrayList<Connection>(identifiers.size());
// Add all connnections which exist according to get()
for (String identifier : identifiers) {
Connection connection = get(identifier);
if (connection != null)
matchingConnections.add(connection);
}
return Collections.unmodifiableCollection(matchingConnections);
}
@Override
public Set<String> getIdentifiers() throws GuacamoleException {
return Collections.unmodifiableSet(shareKeys);
}
@Override
public void add(Connection object) throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied.");
}
@Override
public void update(Connection object) throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied.");
}
@Override
public void remove(String identifier) throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied.");
}
}

View File

@@ -0,0 +1,144 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.jdbc.sharing.connectiongroup;
import org.apache.guacamole.auth.jdbc.sharing.user.SharedUserContext;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.protocol.GuacamoleClientInformation;
/**
* A ConnectionGroup implementation which contains all connections accessible
* via a given SharedUserContext. The identifier of a SharedRootConnectionGroup
* is statically defined, and all Connections which are intended to be contained
* within an instance of SharedRootConnectionGroup MUST return that identifier
* via getParentIdentifier().
*
* @author Michael Jumper
*/
public class SharedRootConnectionGroup implements ConnectionGroup {
/**
* The identifier of the root connection group. All Connections which are
* intended to be contained within an instance of SharedRootConnectionGroup
* MUST return this identifier via getParentIdentifier().
*/
public static final String IDENTIFIER = "ROOT";
/**
* The SharedUserContext through which this connection group is accessible.
*/
private final SharedUserContext userContext;
/**
* Creates a new SharedRootConnectionGroup which contains all connections
* accessible via the given SharedUserContext. The SharedRootConnectionGroup
* is backed by the SharedUserContext, and any changes to the connections
* within the SharedUserContext are immediately reflected in the
* SharedRootConnectionGroup.
*
* @param userContext
* The SharedUserContext which should back the new
* SharedRootConnectionGroup.
*/
public SharedRootConnectionGroup(SharedUserContext userContext) {
this.userContext = userContext;
}
@Override
public String getIdentifier() {
return IDENTIFIER;
}
@Override
public void setIdentifier(String identifier) {
throw new UnsupportedOperationException("The root group is immutable.");
}
@Override
public String getName() {
return IDENTIFIER;
}
@Override
public void setName(String name) {
throw new UnsupportedOperationException("The root group is immutable.");
}
@Override
public String getParentIdentifier() {
return IDENTIFIER;
}
@Override
public void setParentIdentifier(String parentIdentifier) {
throw new UnsupportedOperationException("The root group is immutable.");
}
@Override
public GuacamoleTunnel connect(GuacamoleClientInformation info)
throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied.");
}
@Override
public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap();
}
@Override
public void setAttributes(Map<String, String> attributes) {
// Do nothing - no attributes supported
}
@Override
public int getActiveConnections() {
return 0;
}
@Override
public void setType(Type type) {
throw new UnsupportedOperationException("The root group is immutable.");
}
@Override
public Type getType() {
return Type.BALANCING;
}
@Override
public Set<String> getConnectionIdentifiers() throws GuacamoleException {
Directory<Connection> connectionDirectory = userContext.getConnectionDirectory();
return connectionDirectory.getIdentifiers();
}
@Override
public Set<String> getConnectionGroupIdentifiers() throws GuacamoleException {
return Collections.<String>emptySet();
}
}

View File

@@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.jdbc.sharing.permission;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet;
/**
* An immutable ObjectPermissionSet which defines only READ permissions for a
* fixed set of identifiers.
*
* @author Michael Jumper
*/
public class SharedObjectPermissionSet extends SimpleObjectPermissionSet {
/**
* Returns a new Set of ObjectPermissions defining READ access for each of
* the given identifiers.
*
* @param identifiers
* The identifiers of the objects for which READ permission should be
* granted.
*
* @return
* A new Set of ObjectPermissions granting READ access for each of the
* given identifiers.
*/
private static Set<ObjectPermission> getPermissions(Collection<String> identifiers) {
// Include one READ permission for each of the given identifiers
Set<ObjectPermission> permissions = new HashSet<ObjectPermission>();
for (String identifier : identifiers)
permissions.add(new ObjectPermission(ObjectPermission.Type.READ, identifier));
return permissions;
}
/**
* Creates a new SharedObjectPermissionSet which grants read-only access to
* the objects having the given identifiers. No other permissions are
* granted.
*
* @param identifiers
* The identifiers of the objects for which READ access should be
* granted.
*/
public SharedObjectPermissionSet(Collection<String> identifiers) {
super(getPermissions(identifiers));
}
}

View File

@@ -0,0 +1,105 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.jdbc.sharing.user;
import java.util.UUID;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
/**
* Associates a user with the credentials they used to authenticate, including
* any provided share key.
*
* @author Michael Jumper
*/
public class SharedAuthenticatedUser extends RemoteAuthenticatedUser {
/**
* The username of this user.
*/
private final String identifier;
/**
* The share key which was provided by this user when they authenticated. If
* there is no such share key, this will be null.
*/
private final String shareKey;
/**
* Creates a new SharedAuthenticatedUser which copies the details of the
* given AuthenticatedUser, including that user's identifier (username).
* The new SharedAuthenticatedUser will not have any associated share key.
*
* @param authenticatedUser
* The AuthenticatedUser to copy.
*/
public SharedAuthenticatedUser(AuthenticatedUser authenticatedUser) {
super(authenticatedUser.getAuthenticationProvider(), authenticatedUser.getCredentials());
this.shareKey = null;
this.identifier = authenticatedUser.getIdentifier();
}
/**
* Creates a new SharedAuthenticatedUser associating the given user with
* their corresponding credentials and share key. The identifier (username)
* of the user will be randomly generated.
*
* @param authenticationProvider
* The AuthenticationProvider that has authenticated the given user.
*
* @param credentials
* The credentials given by the user when they authenticated.
*
* @param shareKey
* The share key which was provided by this user when they
* authenticated, or null if no share key was provided.
*/
public SharedAuthenticatedUser(AuthenticationProvider authenticationProvider,
Credentials credentials, String shareKey) {
super(authenticationProvider, credentials);
this.shareKey = shareKey;
this.identifier = UUID.randomUUID().toString();
}
/**
* Returns the share key which was provided by this user when they
* authenticated. If there is no such share key, null is returned.
*
* @return
* The share key which was provided by this user when they
* authenticated, or null if no share key was provided.
*/
public String getShareKey() {
return shareKey;
}
@Override
public String getIdentifier() {
return identifier;
}
@Override
public void setIdentifier(String identifier) {
throw new UnsupportedOperationException("Users authenticated via share keys are immutable.");
}
}

View File

@@ -0,0 +1,136 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.jdbc.sharing.user;
import java.util.Collections;
import java.util.Map;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.sharing.permission.SharedObjectPermissionSet;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet;
import org.apache.guacamole.net.auth.simple.SimpleSystemPermissionSet;
/**
* An immutable implementation of User which defines READ permission for each of
* the objects accessible through the various directories of a given
* SharedUserContext.
*
* @author Michael Jumper
*/
public class SharedUser implements User {
/**
* The AuthenticatedUser that this SharedUser represents.
*/
private final AuthenticatedUser user;
/**
* The SharedUserContext which should be used to define which objects this
* SharedUser has READ permission for.
*/
private final SharedUserContext userContext;
/**
* Creates a new SharedUser whose identity is defined by the given
* AuthenticatedUser, and who has strictly READ access to all objects
* accessible via the various directories of the given SharedUserContext.
*
* @param user
* The AuthenticatedUser that the SharedUser should represent.
*
* @param userContext
* The SharedUserContext which should be used to define which objects
* the SharedUser has READ permission for.
*/
public SharedUser(AuthenticatedUser user, SharedUserContext userContext) {
this.user = user;
this.userContext = userContext;
}
@Override
public String getIdentifier() {
return user.getIdentifier();
}
@Override
public void setIdentifier(String identifier) {
throw new UnsupportedOperationException("Users authenticated via share keys are immutable.");
}
@Override
public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap();
}
@Override
public void setAttributes(Map<String, String> attributes) {
// Do nothing - no attributes supported
}
@Override
public String getPassword() {
return null;
}
@Override
public void setPassword(String password) {
throw new UnsupportedOperationException("Users authenticated via share keys are immutable.");
}
@Override
public SystemPermissionSet getSystemPermissions() throws GuacamoleException {
return new SimpleSystemPermissionSet();
}
@Override
public ObjectPermissionSet getConnectionPermissions() throws GuacamoleException {
Directory<Connection> connectionDirectory = userContext.getConnectionDirectory();
return new SharedObjectPermissionSet(connectionDirectory.getIdentifiers());
}
@Override
public ObjectPermissionSet getConnectionGroupPermissions() throws GuacamoleException {
Directory<ConnectionGroup> connectionGroupDirectory = userContext.getConnectionGroupDirectory();
return new SharedObjectPermissionSet(connectionGroupDirectory.getIdentifiers());
}
@Override
public ObjectPermissionSet getUserPermissions() throws GuacamoleException {
Directory<User> userDirectory = userContext.getUserDirectory();
return new SharedObjectPermissionSet(userDirectory.getIdentifiers());
}
@Override
public ObjectPermissionSet getSharingProfilePermissions() throws GuacamoleException {
return new SimpleObjectPermissionSet();
}
@Override
public ObjectPermissionSet getActiveConnectionPermissions() throws GuacamoleException {
return new SimpleObjectPermissionSet();
}
}

View File

@@ -17,13 +17,15 @@
* under the License. * under the License.
*/ */
package org.apache.guacamole.auth.jdbc.sharing; package org.apache.guacamole.auth.jdbc.sharing.user;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDirectory;
import org.apache.guacamole.auth.jdbc.sharing.connectiongroup.SharedRootConnectionGroup;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.form.Form; import org.apache.guacamole.form.Form;
import org.apache.guacamole.net.auth.ActiveConnection; import org.apache.guacamole.net.auth.ActiveConnection;
import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticationProvider;
@@ -34,32 +36,23 @@ import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.SharingProfile; import org.apache.guacamole.net.auth.SharingProfile;
import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.UserContext; 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.SimpleConnectionGroupDirectory;
import org.apache.guacamole.net.auth.simple.SimpleConnectionRecordSet; import org.apache.guacamole.net.auth.simple.SimpleConnectionRecordSet;
import org.apache.guacamole.net.auth.simple.SimpleDirectory; import org.apache.guacamole.net.auth.simple.SimpleDirectory;
import org.apache.guacamole.net.auth.simple.SimpleUser;
import org.apache.guacamole.net.auth.simple.SimpleUserDirectory; import org.apache.guacamole.net.auth.simple.SimpleUserDirectory;
/** /**
* The user context of a SharedConnectionUser, providing access ONLY to the * The user context of a SharedUser, providing access ONLY to the user
* user themselves, the single SharedConnection associated with that user, and * themselves, the any SharedConnections associated with that user via share
* an internal root connection group containing only that single * keys, and an internal root connection group containing only those
* SharedConnection. * connections.
* *
* @author Michael Jumper * @author Michael Jumper
*/ */
public class SharedConnectionUserContext implements UserContext { public class SharedUserContext implements UserContext {
/** /**
* Provider for retrieving SharedConnection instances. * The AuthenticationProvider that created this SharedUserContext.
*/
@Inject
private Provider<SharedConnection> connectionProvider;
/**
* The AuthenticationProvider that created this SharedConnectionUserContext.
*/ */
private AuthenticationProvider authProvider; private AuthenticationProvider authProvider;
@@ -72,7 +65,8 @@ public class SharedConnectionUserContext implements UserContext {
* A directory of all connections visible to the user for whom this user * A directory of all connections visible to the user for whom this user
* context was created. * context was created.
*/ */
private Directory<Connection> connectionDirectory; @Inject
private SharedConnectionDirectory connectionDirectory;
/** /**
* A directory of all connection groups visible to the user for whom this * A directory of all connection groups visible to the user for whom this
@@ -94,56 +88,50 @@ public class SharedConnectionUserContext implements UserContext {
private ConnectionGroup rootGroup; private ConnectionGroup rootGroup;
/** /**
* Creates a new SharedConnectionUserContext which provides access ONLY to * Creates a new SharedUserContext which provides access ONLY to the given
* the given user, the single SharedConnection associated with that user, * user, the SharedConnections associated with the share keys used by that
* and an internal root connection group containing only that single * user, and an internal root connection group containing only those
* SharedConnection. * SharedConnections.
*
* @param authProvider
* The AuthenticationProvider that created this
* SharedUserContext;
* *
* @param user * @param user
* The SharedConnectionUser for whom this SharedConnectionUserContext * The RemoteAuthenticatedUser for whom this SharedUserContext is being
* is being created. * created.
*/ */
public void init(SharedConnectionUser user) { public void init(AuthenticationProvider authProvider, RemoteAuthenticatedUser 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());
// Associate the originating authentication provider // Associate the originating authentication provider
this.authProvider = user.getAuthenticationProvider(); this.authProvider = authProvider;
// The connection directory should contain only the shared connection // Provide access to all connections shared with the given user
this.connectionDirectory = new SimpleConnectionDirectory( this.connectionDirectory.init(user);
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 // The connection group directory contains only the root group
this.rootGroup = new SharedRootConnectionGroup(this);
this.connectionGroupDirectory = new SimpleConnectionGroupDirectory( this.connectionGroupDirectory = new SimpleConnectionGroupDirectory(
Collections.singletonList(this.rootGroup)); Collections.singletonList(this.rootGroup));
// The user directory contains only this user // The user directory contains only this user
this.self = new SharedUser(user, this);
this.userDirectory = new SimpleUserDirectory(this.self); this.userDirectory = new SimpleUserDirectory(this.self);
} }
/**
* Registers a new share key with this SharedUserContext such that the user
* will have access to the connection associated with that share key. The
* share key will be automatically de-registered when it is no longer valid.
*
* @param shareKey
* The share key to register.
*/
public void registerShareKey(String shareKey) {
connectionDirectory.registerShareKey(shareKey);
}
@Override @Override
public User self() { public User self() {
return self; return self;

View File

@@ -55,8 +55,7 @@ import org.apache.guacamole.token.StandardTokens;
import org.apache.guacamole.token.TokenFilter; import org.apache.guacamole.token.TokenFilter;
import org.mybatis.guice.transactional.Transactional; import org.mybatis.guice.transactional.Transactional;
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper; import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionDefinition; import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUser;
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper;
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterModel; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterModel;
@@ -695,7 +694,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
@Override @Override
@Transactional @Transactional
public GuacamoleTunnel getGuacamoleTunnel(SharedConnectionUser user, public GuacamoleTunnel getGuacamoleTunnel(RemoteAuthenticatedUser user,
SharedConnectionDefinition definition, SharedConnectionDefinition definition,
GuacamoleClientInformation info) GuacamoleClientInformation info)
throws GuacamoleException { throws GuacamoleException {

View File

@@ -24,8 +24,8 @@ import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionDefinition; import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUser; import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.ConnectionGroup;
@@ -173,7 +173,7 @@ public interface GuacamoleTunnelService {
* If the connection cannot be established due to concurrent usage * If the connection cannot be established due to concurrent usage
* rules. * rules.
*/ */
GuacamoleTunnel getGuacamoleTunnel(SharedConnectionUser user, GuacamoleTunnel getGuacamoleTunnel(RemoteAuthenticatedUser user,
SharedConnectionDefinition definition, SharedConnectionDefinition definition,
GuacamoleClientInformation info) GuacamoleClientInformation info)
throws GuacamoleException; throws GuacamoleException;