GUACAMOLE-5: Merge sharing profile API changes.

This commit is contained in:
James Muehlner
2016-07-17 15:27:00 -07:00
53 changed files with 2517 additions and 44 deletions

View File

@@ -20,11 +20,14 @@
package org.apache.guacamole.auth.jdbc.activeconnection;
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.tunnel.ActiveConnectionRecord;
import org.apache.guacamole.auth.jdbc.user.AuthenticatedUser;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.ActiveConnection;
import org.apache.guacamole.net.auth.credentials.UserCredentials;
/**
* An implementation of the ActiveConnection object which has an associated
@@ -44,6 +47,11 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC
*/
private String connectionIdentifier;
/**
* The identifier of the associated sharing profile.
*/
private String sharingProfileIdentifier;
/**
* The date and time this active connection began.
*/
@@ -90,9 +98,10 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC
super.init(currentUser);
// Copy all non-sensitive data from given record
this.connectionIdentifier = activeConnectionRecord.getConnection().getIdentifier();
this.identifier = activeConnectionRecord.getUUID().toString();
this.startDate = activeConnectionRecord.getStartDate();
this.connectionIdentifier = activeConnectionRecord.getConnectionIdentifier();
this.sharingProfileIdentifier = activeConnectionRecord.getSharingProfileIdentifier();
this.identifier = activeConnectionRecord.getUUID().toString();
this.startDate = activeConnectionRecord.getStartDate();
// Include sensitive data, too, if requested
if (includeSensitiveInformation) {
@@ -123,6 +132,22 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC
this.connectionIdentifier = connnectionIdentifier;
}
@Override
public String getSharingProfileIdentifier() {
return sharingProfileIdentifier;
}
@Override
public void setSharingProfileIdentifier(String sharingProfileIdentifier) {
this.sharingProfileIdentifier = sharingProfileIdentifier;
}
@Override
public UserCredentials getSharingCredentials(String identifier)
throws GuacamoleException {
throw new GuacamoleSecurityException("Permission denied");
}
@Override
public Date getStartDate() {
return startDate;

View File

@@ -27,6 +27,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
@@ -156,6 +157,12 @@ public class ModeledConnection extends ModeledGroupedDirectoryObject<ConnectionM
}
@Override
public Set<String> getSharingProfileIdentifiers()
throws GuacamoleException {
return Collections.<String>emptySet();
}
@Override
public List<? extends ConnectionRecord> getHistory() throws GuacamoleException {
return connectionService.retrieveHistory(getCurrentUser(), this);

View File

@@ -58,6 +58,16 @@ public class ModeledConnectionRecord implements ConnectionRecord {
return model.getConnectionName();
}
@Override
public String getSharingProfileIdentifier() {
return null;
}
@Override
public String getSharingProfileName() {
return null;
}
@Override
public Date getStartDate() {
return model.getStartDate();

View File

@@ -171,6 +171,16 @@ public class ActiveConnectionRecord implements ConnectionRecord {
return connection.getName();
}
@Override
public String getSharingProfileIdentifier() {
return null;
}
@Override
public String getSharingProfileName() {
return null;
}
@Override
public Date getStartDate() {
return startDate;

View File

@@ -49,6 +49,7 @@ import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
import org.apache.guacamole.net.auth.permission.SystemPermission;
import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -251,6 +252,12 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
return connectionGroupPermissionService.getPermissionSet(getCurrentUser(), this);
}
@Override
public ObjectPermissionSet getSharingProfilePermissions()
throws GuacamoleException {
return new SimpleObjectPermissionSet();
}
@Override
public ObjectPermissionSet getActiveConnectionPermissions()
throws GuacamoleException {

View File

@@ -26,6 +26,7 @@ import org.apache.guacamole.auth.jdbc.connection.ConnectionDirectory;
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.auth.jdbc.base.RestrictedObject;
import org.apache.guacamole.auth.jdbc.activeconnection.ActiveConnectionDirectory;
@@ -38,7 +39,9 @@ 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.Directory;
import org.apache.guacamole.net.auth.SharingProfile;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.simple.SimpleDirectory;
/**
* UserContext implementation which is driven by an arbitrary, underlying
@@ -134,6 +137,12 @@ public class UserContext extends RestrictedObject
return connectionGroupDirectory;
}
@Override
public Directory<SharingProfile> getSharingProfileDirectory()
throws GuacamoleException {
return new SimpleDirectory<SharingProfile>();
}
@Override
public Directory<ActiveConnection> getActiveConnectionDirectory()
throws GuacamoleException {
@@ -173,4 +182,9 @@ public class UserContext extends RestrictedObject
return ModeledConnectionGroup.ATTRIBUTES;
}
@Override
public Collection<Form> getSharingProfileAttributes() {
return Collections.<Form>emptyList();
}
}

View File

@@ -34,6 +34,7 @@ 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.simple.SimpleConnectionGroup;
import org.apache.guacamole.net.auth.simple.SimpleConnectionGroupDirectory;
@@ -193,6 +194,12 @@ public class UserContext implements org.apache.guacamole.net.auth.UserContext {
return new SimpleDirectory<ActiveConnection>();
}
@Override
public Directory<SharingProfile> getSharingProfileDirectory()
throws GuacamoleException {
return new SimpleDirectory<SharingProfile>();
}
@Override
public ConnectionRecordSet getConnectionHistory()
throws GuacamoleException {
@@ -214,4 +221,9 @@ public class UserContext implements org.apache.guacamole.net.auth.UserContext {
return Collections.<Form>emptyList();
}
@Override
public Collection<Form> getSharingProfileAttributes() {
return Collections.<Form>emptyList();
}
}

View File

@@ -99,6 +99,12 @@ public class Field {
*/
public static String TIME = "TIME";
/**
* An HTTP query parameter which is expected to be embedded in the URL
* given to a user.
*/
public static String QUERY_PARAMETER = "QUERY_PARAMETER";
}
/**

View File

@@ -36,6 +36,11 @@ public abstract class AbstractActiveConnection extends AbstractIdentifiable
*/
private String connectionIdentifier;
/**
* The identifier of the associated sharing profile.
*/
private String sharingProfileIdentifier;
/**
* The date and time this active connection began.
*/
@@ -66,6 +71,16 @@ public abstract class AbstractActiveConnection extends AbstractIdentifiable
this.connectionIdentifier = connnectionIdentifier;
}
@Override
public String getSharingProfileIdentifier() {
return sharingProfileIdentifier;
}
@Override
public void setSharingProfileIdentifier(String sharingProfileIdentifier) {
this.sharingProfileIdentifier = sharingProfileIdentifier;
}
@Override
public Date getStartDate() {
return startDate;

View File

@@ -19,6 +19,9 @@
package org.apache.guacamole.net.auth;
import java.util.Collections;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.protocol.GuacamoleConfiguration;
/**
@@ -76,4 +79,10 @@ public abstract class AbstractConnection extends AbstractIdentifiable
this.configuration = configuration;
}
@Override
public Set<String> getSharingProfileIdentifiers()
throws GuacamoleException {
return Collections.<String>emptySet();
}
}

View File

@@ -0,0 +1,119 @@
/*
* 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.net.auth;
import java.util.HashMap;
import java.util.Map;
/**
* Base implementation of a sharing profile which can be used to share a
* Guacamole connection.
*
* @author Michael Jumper
*/
public abstract class AbstractSharingProfile implements SharingProfile {
/**
* The human-readable name of this sharing profile.
*/
private String name;
/**
* The unique identifier associated with this sharing profile.
*/
private String identifier;
/**
* The identifier of the primary connection that this sharing profile can
* be used to share.
*/
private String primaryConnectionIdentifier;
/**
* All connection parameters with this sharing profile.
*/
private final Map<String, String> parameters = new HashMap<String, String>();
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getIdentifier() {
return identifier;
}
@Override
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
@Override
public String getPrimaryConnectionIdentifier() {
return primaryConnectionIdentifier;
}
@Override
public void setPrimaryConnectionIdentifier(String primaryConnectionIdentifier) {
this.primaryConnectionIdentifier = primaryConnectionIdentifier;
}
@Override
public Map<String, String> getParameters() {
return parameters;
}
@Override
public void setParameters(Map<String, String> parameters) {
this.parameters.clear();
this.parameters.putAll(parameters);
}
@Override
public int hashCode() {
if (identifier == null) return 0;
return identifier.hashCode();
}
@Override
public boolean equals(Object obj) {
// Not equal if null or not an SharingProfile
if (obj == null) return false;
if (!(obj instanceof AbstractSharingProfile)) return false;
// Get identifier
String objIdentifier = ((AbstractSharingProfile) obj).identifier;
// If null, equal only if this identifier is null
if (objIdentifier == null) return identifier == null;
// Otherwise, equal only if strings are identical
return objIdentifier.equals(identifier);
}
}

View File

@@ -28,7 +28,7 @@ import org.apache.guacamole.net.GuacamoleTunnel;
*
* @author Michael Jumper
*/
public interface ActiveConnection extends Identifiable {
public interface ActiveConnection extends Identifiable, Shareable<SharingProfile> {
/**
* Returns the identifier of the connection being actively used. Unlike the
@@ -47,7 +47,24 @@ public interface ActiveConnection extends Identifiable {
* The identifier of the connection being actively used.
*/
void setConnectionIdentifier(String connnectionIdentifier);
/**
* Returns the identifier of the sharing profile being actively used. If
* the connection is being accessed directly, this will be null.
*
* @return
* The identifier of the sharing profile being actively used.
*/
String getSharingProfileIdentifier();
/**
* Sets the identifier of the sharing profile being actively used.
*
* @param sharingProfileIdentifier
* The identifier of the sharing profile being actively used.
*/
void setSharingProfileIdentifier(String sharingProfileIdentifier);
/**
* Returns the date and time the connection began.
*

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.net.auth;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.protocol.GuacamoleConfiguration;
@@ -119,4 +120,21 @@ public interface Connection extends Identifiable, Connectable {
*/
public List<? extends ConnectionRecord> getHistory() throws GuacamoleException;
/**
* Returns identifiers of all readable sharing profiles that can be used to
* join this connection when it is active. The level of access granted to a
* joining user is dictated by the connection parameters associated with
* the sharing profile, not necessarily the parameters of the primary
* connection being joined.
*
* @return
* A Set of identifiers representing the sharing profiles for this
* connection.
*
* @throws GuacamoleException
* If an error occurs while fetching the sharing profiles for this
* connection.
*/
public Set<String> getSharingProfileIdentifiers() throws GuacamoleException;
}

View File

@@ -48,6 +48,32 @@ public interface ConnectionRecord {
*/
public String getConnectionName();
/**
* Returns the identifier of 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 identifier of the sharing profile used to access the connection
* associated with this connection record, or null if the connection
* was accessed directly.
*/
public String getSharingProfileIdentifier();
/**
* Returns the name of 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 name of the sharing profile used to access the connection
* associated with this connection record, or null if the connection
* was accessed directly.
*/
public String getSharingProfileName();
/**
* Returns the date and time the connection began.
*

View File

@@ -0,0 +1,63 @@
/*
* 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.net.auth;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.credentials.UserCredentials;
/**
* An object which can be shared with others via specially-generated sets of
* credentials. It is expected, but not required, that these credentials are
* temporary.
*
* @param <T>
* The type of object which dictates the semantics/restrictions of shared
* objects.
*
* @author Michael Jumper
*/
public interface Shareable<T> {
/**
* Returns a full set of credentials which can be used to authenticate as a
* user with access strictly to this object. The semantics and restrictions
* of the shared object (when accessed using the returned sharing
* credentials) are defined by the {@link T} associated with the given
* identifier and within the
* {@link Directory}&lt;{@link T}&gt; of the same {@link UserContext} that
* this Shareable was retrieved from.
*
* @param identifier
* The identifier of a {@link T} within the
* {@link Directory}&lt;{@link T}&gt; of the same {@link UserContext}
* that this Shareable was retrieved from.
*
* @return
* A full set of credentials which can be used to authenticate and
* obtain access to this object.
*
* @throws GuacamoleException
* If credentials could not be generated, or permission to share this
* object is denied.
*/
public UserCredentials getSharingCredentials(String identifier)
throws GuacamoleException;
}

View File

@@ -0,0 +1,119 @@
/*
* 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.net.auth;
import java.util.Map;
/**
* Represents the semantics which apply to an existing connection when shared,
* along with a human-readable name and unique identifier.
*
* @author Michael Jumper
*/
public interface SharingProfile extends Identifiable {
/**
* Returns the human-readable name assigned to this SharingProfile.
*
* @return
* The name assigned to this SharingProfile.
*/
public String getName();
/**
* Sets the human-readable name assigned to this SharingProfile.
*
* @param name
* The name to assign.
*/
public void setName(String name);
/**
* Returns the identifier of the primary connection associated with this
* connection. The primary connection is the connection that this sharing
* profile can be used to share.
*
* @return
* The identifier of the primary connection associated with this
* connection.
*/
public String getPrimaryConnectionIdentifier();
/**
* Sets the identifier of the primary connection associated with this
* connection. The primary connection is the connection that this sharing
* profile can be used to share.
*
* @param identifier
* The identifier of the primary connection associated with this
* connection.
*/
public void setPrimaryConnectionIdentifier(String identifier);
/**
* Returns a map which contains connection parameter name/value pairs as
* key/value pairs. Changes to this map will affect the parameters stored
* within this sharing profile. The differences in these parameters compared
* to those of the associated primary connection yield different levels of
* access to users joining the primary connection via this sharing profile.
* Note that because configurations may contain sensitive information, some
* data in this map may be omitted or tokenized.
*
* @return
* A map which contains all connection parameter name/value pairs as
* key/value pairs.
*/
public Map<String, String> getParameters();
/**
* Replaces all current parameters with the parameters defined within the
* given map. Key/value pairs within the map represent parameter name/value
* pairs. The differences in these parameters compared to those of the
* associated primary connection yield different levels of access to users
* joining the primary connection via this sharing profile.
*
* @param parameters
* A map which contains all connection parameter name/value pairs as
* key/value pairs.
*/
public void setParameters(Map<String, String> parameters);
/**
* Returns all attributes associated with this sharing profile. The returned
* map may not be modifiable.
*
* @return
* A map of all attribute identifiers to their corresponding values,
* for all attributes associated with this sharing profile, which may
* not be modifiable.
*/
Map<String, String> getAttributes();
/**
* Sets the given attributes. If an attribute within the map is not
* supported, it will simply be dropped. Any attributes not within the
* given map will be left untouched.
*
* @param attributes
* A map of all attribute identifiers to their corresponding values.
*/
void setAttributes(Map<String, String> attributes);
}

View File

@@ -111,6 +111,20 @@ public interface User extends Identifiable {
ObjectPermissionSet getConnectionGroupPermissions()
throws GuacamoleException;
/**
* Returns all sharing profile permissions given to this user.
*
* @return
* An ObjectPermissionSet of all sharing profile permissions granted to
* this user.
*
* @throws GuacamoleException
* If an error occurs while retrieving permissions, or if reading all
* permissions is not allowed.
*/
ObjectPermissionSet getSharingProfilePermissions()
throws GuacamoleException;
/**
* Returns all permissions given to this user regarding currently-active
* connections.

View File

@@ -106,6 +106,21 @@ public interface UserContext {
Directory<ActiveConnection> getActiveConnectionDirectory()
throws GuacamoleException;
/**
* Retrieves a Directory which can be used to view and manipulate
* sharing profiles and their configurations, but only as allowed by the
* permissions given to the user.
*
* @return
* A Directory whose operations are bound by the permissions of the
* user.
*
* @throws GuacamoleException
* If an error occurs while creating the Directory.
*/
Directory<SharingProfile> getSharingProfileDirectory()
throws GuacamoleException;
/**
* Retrieves all connection records visible to current user. The resulting
* set of connection records can be further filtered and ordered using the
@@ -165,4 +180,15 @@ public interface UserContext {
*/
Collection<Form> getConnectionGroupAttributes();
/**
* Retrieves a collection of all attributes applicable to sharing profiles.
* This collection will contain only those attributes which the current user
* has general permission to view or modify. If there are no such
* attributes, this collection will be empty.
*
* @return
* A collection of all attributes applicable to sharing profile.
*/
Collection<Form> getSharingProfileAttributes();
}

View File

@@ -0,0 +1,220 @@
/*
* 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.net.auth.credentials;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.guacamole.form.Field;
/**
* A fully-valid set of credentials and associated values. Each instance of
* this object should describe a full set of parameter name/value pairs which
* can be used to authenticate successfully, even if that success depends on
* factors not described by this object.
*
* @author Michael Jumper
*/
public class UserCredentials extends CredentialsInfo {
/**
* All fields required for valid credentials.
*/
private Map<String, String> values;
/**
* Creates a new UserCredentials object which requires the given fields and
* values.
*
* @param fields
* The fields to require.
*
* @param values
* The values required for each field, as a map of field name to
* correct value.
*/
public UserCredentials(Collection<Field> fields, Map<String, String> values) {
super(fields);
this.values = values;
}
/**
* Creates a new UserCredentials object which requires fields described by
* the given CredentialsInfo. The value required for each field in the
* CredentialsInfo is defined in the given Map.
*
* @param info
* The CredentialsInfo object describing the fields to require.
*
* @param values
* The values required for each field, as a map of field name to
* correct value.
*/
public UserCredentials(CredentialsInfo info, Map<String, String> values) {
this(info.getFields(), values);
}
/**
* Creates a new UserCredentials object which requires fields described by
* the given CredentialsInfo but does not yet have any defined values.
*
* @param info
* The CredentialsInfo object describing the fields to require.
*/
public UserCredentials(CredentialsInfo info) {
this(info, new HashMap<String, String>());
}
/**
* Creates a new UserCredentials object which requires the given fields but
* does not yet have any defined values.
*
* @param fields
* The fields to require.
*/
public UserCredentials(Collection<Field> fields) {
this(fields, new HashMap<String, String>());
}
/**
* Returns a map of field names to values which backs this UserCredentials
* object. Modifications to the returned map will directly affect the
* associated name/value pairs.
*
* @return
* A map of field names to their corresponding values which backs this
* UserCredentials object.
*/
public Map<String, String> getValues() {
return values;
}
/**
* Replaces the map backing this UserCredentials object with the given map.
* All field name/value pairs described by the original map are replaced by
* the name/value pairs in the given map.
*
* @param values
* The map of field names to their corresponding values which should be
* used to back this UserCredentials object.
*/
public void setValues(Map<String, String> values) {
this.values = values;
}
/**
* Returns the value defined by this UserCrendentials object for the field
* having the given name.
*
* @param name
* The name of the field whose value should be returned.
*
* @return
* The value of the field having the given name, or null if no value is
* defined for that field.
*/
public String getValue(String name) {
return values.get(name);
}
/**
* Returns the value defined by this UserCrendentials object for the given
* field.
*
* @param field
* The field whose value should be returned.
*
* @return
* The value of the given field, or null if no value is defined for
* that field.
*/
public String getValue(Field field) {
return getValue(field.getName());
}
/**
* Sets the value of the field having the given name. Any existing value
* for that field is replaced.
*
* @param name
* The name of the field whose value should be assigned.
*
* @param value
* The value to assign to the field having the given name.
*
* @return
* The previous value of the field, or null if the value of the field
* was not previously defined.
*/
public String setValue(String name, String value) {
return values.put(name, value);
}
/**
* Sets the value of the given field. Any existing value for that field is
* replaced.
*
* @param field
* The field whose value should be assigned.
*
* @param value
* The value to assign to the given field.
*
* @return
* The previous value of the field, or null if the value of the field
* was not previously defined.
*/
public String setValue(Field field, String value) {
return setValue(field.getName(), value);
}
/**
* Removes (undefines) the value of the field having the given name,
* returning its previous value. If the field value was not defined, this
* function has no effect, and null is returned.
*
* @param name
* The name of the field whose value should be removed.
*
* @return
* The previous value of the field, or null if the value of the field
* was not previously defined.
*/
public String removeValue(String name) {
return values.remove(name);
}
/**
* Removes (undefines) the value of the given field returning its previous
* value. If the field value was not defined, this function has no effect,
* and null is returned.
*
* @param field
* The field whose value should be removed.
*
* @return
* The previous value of the field, or null if the value of the field
* was not previously defined.
*/
public String removeValue(Field field) {
return removeValue(field.getName());
}
}

View File

@@ -49,6 +49,11 @@ public class SystemPermission implements Permission<SystemPermission.Type> {
*/
CREATE_CONNECTION_GROUP,
/**
* Create sharing profiles.
*/
CREATE_SHARING_PROFILE,
/**
* Administer the system in general, including adding permissions
* which affect the system (like user creation, connection creation,

View File

@@ -195,4 +195,9 @@ public class SimpleUser extends AbstractUser {
return new SimpleObjectPermissionSet();
}
@Override
public ObjectPermissionSet getSharingProfilePermissions() {
return new SimpleObjectPermissionSet();
}
}

View File

@@ -32,6 +32,7 @@ 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.protocol.GuacamoleConfiguration;
@@ -192,6 +193,12 @@ public class SimpleUserContext implements UserContext {
return rootGroup;
}
@Override
public Directory<SharingProfile> getSharingProfileDirectory()
throws GuacamoleException {
return new SimpleDirectory<SharingProfile>();
}
@Override
public Directory<ActiveConnection> getActiveConnectionDirectory()
throws GuacamoleException {
@@ -219,4 +226,9 @@ public class SimpleUserContext implements UserContext {
return Collections.<Form>emptyList();
}
@Override
public Collection<Form> getSharingProfileAttributes() {
return Collections.<Form>emptyList();
}
}

View File

@@ -28,7 +28,7 @@ import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.tunnel.StreamInterceptingTunnel;
import org.apache.guacamole.tunnel.UserTunnel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -59,8 +59,8 @@ public class GuacamoleSession {
/**
* All currently-active tunnels, indexed by tunnel UUID.
*/
private final Map<String, StreamInterceptingTunnel> tunnels =
new ConcurrentHashMap<String, StreamInterceptingTunnel>();
private final Map<String, UserTunnel> tunnels =
new ConcurrentHashMap<String, UserTunnel>();
/**
* The last time this session was accessed.
@@ -196,7 +196,7 @@ public class GuacamoleSession {
*
* @return A map of all active tunnels associated with this session.
*/
public Map<String, StreamInterceptingTunnel> getTunnels() {
public Map<String, UserTunnel> getTunnels() {
return tunnels;
}
@@ -206,7 +206,7 @@ public class GuacamoleSession {
*
* @param tunnel The tunnel to associate with this session.
*/
public void addTunnel(StreamInterceptingTunnel tunnel) {
public void addTunnel(UserTunnel tunnel) {
tunnels.put(tunnel.getUUID().toString(), tunnel);
}

View File

@@ -39,6 +39,9 @@ import org.apache.guacamole.rest.connectiongroup.ConnectionGroupModule;
import org.apache.guacamole.rest.language.LanguageRESTService;
import org.apache.guacamole.rest.patch.PatchRESTService;
import org.apache.guacamole.rest.session.SessionResourceFactory;
import org.apache.guacamole.rest.sharingprofile.SharingProfileModule;
import org.apache.guacamole.rest.tunnel.TunnelCollectionResourceFactory;
import org.apache.guacamole.rest.tunnel.TunnelResourceFactory;
import org.apache.guacamole.rest.user.UserModule;
/**
@@ -90,12 +93,15 @@ public class RESTServiceModule extends ServletModule {
// Root-level resources
bind(SessionRESTService.class);
install(new FactoryModuleBuilder().build(SessionResourceFactory.class));
install(new FactoryModuleBuilder().build(TunnelCollectionResourceFactory.class));
install(new FactoryModuleBuilder().build(TunnelResourceFactory.class));
install(new FactoryModuleBuilder().build(UserContextResourceFactory.class));
// Resources below root
install(new ActiveConnectionModule());
install(new ConnectionModule());
install(new ConnectionGroupModule());
install(new SharingProfileModule());
install(new UserModule());
// Set up the servlet and JSON mappings

View File

@@ -0,0 +1,82 @@
/*
* 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.rest.activeconnection;
import java.util.Collection;
import java.util.Map;
import org.apache.guacamole.form.Field;
import org.apache.guacamole.net.auth.credentials.UserCredentials;
/**
* The object returned by REST API calls to define a full set of valid
* credentials, including field definitions and corresponding expected
* values.
*
* @author Michael Jumper
*/
public class APIUserCredentials {
/**
* All expected request parameters, if any, as a collection of fields.
*/
private final Collection<Field> expected;
/**
* A map of all field values by field name.
*/
private final Map<String, String> values;
/**
* Creates a new APIUserCredentials object whose required parameters and
* corresponding values are defined by the given UserCredentials.
*
* @param userCredentials
* The UserCredentials which defines the parameters and corresponding
* values of this APIUserCredentials.
*/
public APIUserCredentials(UserCredentials userCredentials) {
this.expected = userCredentials.getFields();
this.values = userCredentials.getValues();
}
/**
* Returns a collection of all required parameters, where each parameter is
* represented by a field.
*
* @return
* A collection of all required parameters.
*/
public Collection<Field> getExpected() {
return expected;
}
/**
* Returns a map of all field values by field name. The fields having the
* names used within this map should be defined within the collection of
* required parameters returned by getExpected().
*
* @return
* A map of all field values by field name.
*/
public Map<String, String> getValues() {
return values;
}
}

View File

@@ -19,16 +19,24 @@
package org.apache.guacamole.rest.activeconnection;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.ActiveConnection;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.rest.connection.APIConnection;
import org.apache.guacamole.rest.directory.DirectoryObjectResource;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
import org.apache.guacamole.rest.directory.DirectoryResourceFactory;
/**
* A REST resource which abstracts the operations available on an existing
@@ -41,6 +49,25 @@ import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
public class ActiveConnectionResource
extends DirectoryObjectResource<ActiveConnection, APIActiveConnection> {
/**
* The UserContext associated with the Directory which contains the
* Connection exposed by this resource.
*/
private final UserContext userContext;
/**
* The ActiveConnection exposed by this ActiveConnectionResource.
*/
private final ActiveConnection activeConnection;
/**
* A factory which can be used to create instances of resources representing
* Connection.
*/
@Inject
private DirectoryResourceFactory<Connection, APIConnection>
connectionDirectoryResourceFactory;
/**
* Creates a new ActiveConnectionResource which exposes the operations and
* subresources available for the given ActiveConnection.
@@ -51,7 +78,7 @@ public class ActiveConnectionResource
* @param directory
* The Directory which contains the given ActiveConnection.
*
* @param connection
* @param activeConnection
* The ActiveConnection that this ActiveConnectionResource should
* represent.
*
@@ -62,9 +89,58 @@ public class ActiveConnectionResource
@AssistedInject
public ActiveConnectionResource(@Assisted UserContext userContext,
@Assisted Directory<ActiveConnection> directory,
@Assisted ActiveConnection connection,
@Assisted ActiveConnection activeConnection,
DirectoryObjectTranslator<ActiveConnection, APIActiveConnection> translator) {
super(directory, connection, translator);
super(directory, activeConnection, translator);
this.userContext = userContext;
this.activeConnection = activeConnection;
}
/**
* Retrieves a resource representing the Connection object that is being
* actively used.
*
* @return
* A resource representing the Connection object that is being actively
* used.
*
* @throws GuacamoleException
* If an error occurs while retrieving the Connection.
*/
@Path("connection")
public DirectoryObjectResource<Connection, APIConnection> getConnection()
throws GuacamoleException {
// Return the underlying connection as a resource
return connectionDirectoryResourceFactory
.create(userContext, userContext.getConnectionDirectory())
.getObjectResource(activeConnection.getConnectionIdentifier());
}
/**
* Retrieves a set of credentials which can be POSTed by another user to the
* "/api/tokens" endpoint to obtain access strictly to this connection. The
* retrieved credentials may be purpose-generated and temporary.
*
* @param sharingProfileIdentifier The identifier of the sharing connection
* defining the semantics of the shared session.
*
* @return The set of credentials which should be used to access strictly
* this connection.
*
* @throws GuacamoleException If an error occurs while retrieving the
* sharing credentials for this connection.
*/
@GET
@Path("sharingCredentials/{sharingProfile}")
public APIUserCredentials getSharingCredentials(
@PathParam("sharingProfile") String sharingProfileIdentifier)
throws GuacamoleException {
// Generate and return sharing credentials for the active connection
return new APIUserCredentials(activeConnection.getSharingCredentials(sharingProfileIdentifier));
}
}

View File

@@ -22,6 +22,7 @@ package org.apache.guacamole.rest.connection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.Connection;
@@ -122,6 +123,11 @@ public class APIConnectionWrapper implements Connection {
apiConnection.setAttributes(attributes);
}
@Override
public Set<String> getSharingProfileIdentifiers() {
throw new UnsupportedOperationException("Operation not supported.");
}
@Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException {
throw new UnsupportedOperationException("Operation not supported.");

View File

@@ -19,6 +19,7 @@
package org.apache.guacamole.rest.connection;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.util.ArrayList;
@@ -34,6 +35,8 @@ import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionRecord;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.rest.directory.DirectoryView;
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.permission.ObjectPermission;
@@ -44,6 +47,9 @@ import org.apache.guacamole.rest.history.APIConnectionRecord;
import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.apache.guacamole.rest.directory.DirectoryObjectResource;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
import org.apache.guacamole.rest.directory.DirectoryResource;
import org.apache.guacamole.rest.directory.DirectoryResourceFactory;
import org.apache.guacamole.rest.sharingprofile.APISharingProfile;
/**
* A REST resource which abstracts the operations available on an existing
@@ -66,6 +72,14 @@ public class ConnectionResource extends DirectoryObjectResource<Connection, APIC
*/
private final Connection connection;
/**
* A factory which can be used to create instances of resources representing
* SharingProfiles.
*/
@Inject
private DirectoryResourceFactory<SharingProfile, APISharingProfile>
sharingProfileDirectoryResourceFactory;
/**
* Creates a new ConnectionResource which exposes the operations and
* subresources available for the given Connection.
@@ -152,4 +166,33 @@ public class ConnectionResource extends DirectoryObjectResource<Connection, APIC
}
/**
* Returns a resource which provides read-only access to the subset of
* SharingProfiles that the current user can use to share this connection.
*
* @return
* A resource which provides read-only access to the subset of
* SharingProfiles that the current user can use to share this
* connection.
*
* @throws GuacamoleException
* If the SharingProfiles associated with this connection cannot be
* retrieved.
*/
@Path("sharingProfiles")
public DirectoryResource<SharingProfile, APISharingProfile>
getSharingProfileDirectoryResource() throws GuacamoleException {
// Produce subset of all SharingProfiles, containing only those which
// are associated with this connection
Directory<SharingProfile> sharingProfiles = new DirectoryView<SharingProfile>(
userContext.getSharingProfileDirectory(),
connection.getSharingProfileIdentifiers()
);
// Return a new resource which provides access to only those SharingProfiles
return sharingProfileDirectoryResourceFactory.create(userContext, sharingProfiles);
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.rest.directory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleUnsupportedException;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.Identifiable;
/**
* Directory implementation which represents a read-only subset of another
* existing Directory. Access is provided only to a limited set of objects,
* determined by the set of identifiers provided when the DirectoryView is
* created.
*
* @author Michael Jumper
* @param <ObjectType>
* The type of objects accessible through this DirectoryView.
*/
public class DirectoryView<ObjectType extends Identifiable>
implements Directory<ObjectType> {
/**
* The Directory from which the given set of objects will be retrieved.
*/
private final Directory<ObjectType> directory;
/**
* The set of identifiers representing the restricted set of objects that
* this DirectoryView should provide access to.
*/
private final Set<String> identifiers;
/**
* Creates a new DirectoryView which provides access to a read-only subset
* of the objects in the given Directory. Only objects whose identifiers
* are within the provided set will be accessible.
*
* @param directory
* The Directory of which this DirectoryView represents a subset.
*
* @param identifiers
* The identifiers of all objects which should be accessible through
* this DirectoryView. Objects which do not have identifiers within
* the provided set will be inaccessible.
*/
public DirectoryView(Directory<ObjectType> directory,
Set<String> identifiers) {
this.directory = directory;
this.identifiers = identifiers;
}
@Override
public ObjectType get(String identifier) throws GuacamoleException {
// Attempt to retrieve the requested object ONLY if it's within the
// originally-specified subset
if (!identifiers.contains(identifier))
return null;
// Delegate to underlying directory
return directory.get(identifier);
}
@Override
public Collection<ObjectType> getAll(Collection<String> identifiers)
throws GuacamoleException {
// Reduce requested identifiers to only those which occur within the
// originally-specified subset
identifiers = new ArrayList<String>(identifiers);
identifiers.retainAll(this.identifiers);
// Delegate to underlying directory
return directory.getAll(identifiers);
}
@Override
public Set<String> getIdentifiers() throws GuacamoleException {
return identifiers;
}
@Override
public void add(ObjectType object) throws GuacamoleException {
throw new GuacamoleUnsupportedException("Directory view is read-only");
}
@Override
public void update(ObjectType object) throws GuacamoleException {
throw new GuacamoleUnsupportedException("Directory view is read-only");
}
@Override
public void remove(String identifier) throws GuacamoleException {
throw new GuacamoleUnsupportedException("Directory view is read-only");
}
}

View File

@@ -39,6 +39,16 @@ public class APIConnectionRecord {
*/
private final String connectionName;
/**
* The identifier of the sharing profile associated with this record.
*/
private final String sharingProfileIdentifier;
/**
* The identifier of the sharing profile associated with this record.
*/
private final String sharingProfileName;
/**
* The date and time the connection began.
*/
@@ -73,13 +83,15 @@ public class APIConnectionRecord {
* The record to copy data from.
*/
public APIConnectionRecord(ConnectionRecord record) {
this.connectionIdentifier = record.getConnectionIdentifier();
this.connectionName = record.getConnectionName();
this.startDate = record.getStartDate();
this.endDate = record.getEndDate();
this.remoteHost = record.getRemoteHost();
this.username = record.getUsername();
this.active = record.isActive();
this.connectionIdentifier = record.getConnectionIdentifier();
this.connectionName = record.getConnectionName();
this.sharingProfileIdentifier = record.getSharingProfileIdentifier();
this.sharingProfileName = record.getSharingProfileName();
this.startDate = record.getStartDate();
this.endDate = record.getEndDate();
this.remoteHost = record.getRemoteHost();
this.username = record.getUsername();
this.active = record.isActive();
}
/**
@@ -103,6 +115,32 @@ public class APIConnectionRecord {
return connectionName;
}
/**
* Returns the identifier of the sharing profile associated with this
* record. If the connection was not being used via a sharing profile, this
* will be null.
*
* @return
* The identifier of the sharing profile associated with this record,
* or null if no sharing profile was used.
*/
public String getSharingProfileIdentifier() {
return sharingProfileIdentifier;
}
/**
* Returns the name of the sharing profile associated with this record. If
* the connection was not being used via a sharing profile, this will be
* null.
*
* @return
* The name of the sharing profile associated with this record, or null
* if no sharing profile was used.
*/
public String getSharingProfileName() {
return sharingProfileName;
}
/**
* Returns the date and time the connection began.
*

View File

@@ -53,6 +53,12 @@ public class APIPermissionSet {
private Map<String, Set<ObjectPermission.Type>> connectionGroupPermissions =
new HashMap<String, Set<ObjectPermission.Type>>();
/**
* Map of sharing profile ID to the set of granted permissions.
*/
private Map<String, Set<ObjectPermission.Type>> sharingProfilePermissions =
new HashMap<String, Set<ObjectPermission.Type>>();
/**
* Map of active connection ID to the set of granted permissions.
*/
@@ -155,6 +161,7 @@ public class APIPermissionSet {
addSystemPermissions(systemPermissions, user.getSystemPermissions());
addObjectPermissions(connectionPermissions, user.getConnectionPermissions());
addObjectPermissions(connectionGroupPermissions, user.getConnectionGroupPermissions());
addObjectPermissions(sharingProfilePermissions, user.getSharingProfilePermissions());
addObjectPermissions(activeConnectionPermissions, user.getActiveConnectionPermissions());
addObjectPermissions(userPermissions, user.getUserPermissions());
@@ -190,6 +197,21 @@ public class APIPermissionSet {
return connectionGroupPermissions;
}
/**
* Returns a map of sharing profile identifiers to the set of permissions
* granted for that sharing profile. If no permissions are granted to a
* particular sharing profile, its identifier will not be present as a key
* in the map. This map is mutable, and changes to this map will affect the
* permission set directly.
*
* @return
* A map of sharing profile identifiers to the set of permissions
* granted for that sharing profile.
*/
public Map<String, Set<ObjectPermission.Type>> getSharingProfilePermissions() {
return sharingProfilePermissions;
}
/**
* Returns a map of active connection IDs to the set of permissions granted
* for that active connection. If no permissions are granted to a particular
@@ -257,6 +279,19 @@ public class APIPermissionSet {
this.connectionGroupPermissions = connectionGroupPermissions;
}
/**
* Replaces the current map of sharing profile permissions with the given
* map, which must map each sharing profile identifier to its corresponding
* set of granted permissions. If a sharing profile has no permissions, its
* identifier must not be present as a key in the map.
*
* @param sharingProfilePermissions
* The map which must replace the currently-stored map of permissions.
*/
public void setSharingProfilePermissions(Map<String, Set<ObjectPermission.Type>> sharingProfilePermissions) {
this.sharingProfilePermissions = sharingProfilePermissions;
}
/**
* Replaces the current map of active connection permissions with the give
* map, which must map active connection ID to its corresponding set of

View File

@@ -55,6 +55,12 @@ public class PermissionSetResource {
*/
private static final String CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX = "/connectionGroupPermissions/";
/**
* The prefix of any path within an operation of a JSON patch which
* modifies the permissions of a user regarding a specific sharing profile.
*/
private static final String SHARING_PROFILE_PERMISSION_PATCH_PATH_PREFIX = "/sharingProfilePermissions/";
/**
* The prefix of any path within an operation of a JSON patch which
* modifies the permissions of a user regarding a specific active connection.
@@ -170,6 +176,7 @@ public class PermissionSetResource {
// Permission patches for all types of permissions
PermissionSetPatch<ObjectPermission> connectionPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<ObjectPermission> connectionGroupPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<ObjectPermission> sharingProfilePermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<ObjectPermission> activeConnectionPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<ObjectPermission> userPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<SystemPermission> systemPermissionPatch = new PermissionSetPatch<SystemPermission>();
@@ -205,6 +212,19 @@ public class PermissionSetResource {
}
// Create sharing profile permission if path has sharing profile prefix
else if (path.startsWith(SHARING_PROFILE_PERMISSION_PATCH_PATH_PREFIX)) {
// Get identifier and type from patch operation
String identifier = path.substring(SHARING_PROFILE_PERMISSION_PATCH_PATH_PREFIX.length());
ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue());
// Create and update corresponding permission
ObjectPermission permission = new ObjectPermission(type, identifier);
updatePermissionSet(patch.getOp(), sharingProfilePermissionPatch, permission);
}
// Create active connection permission if path has active connection prefix
else if (path.startsWith(ACTIVE_CONNECTION_PERMISSION_PATCH_PATH_PREFIX)) {
@@ -252,6 +272,7 @@ public class PermissionSetResource {
// Save the permission changes
connectionPermissionPatch.apply(user.getConnectionPermissions());
connectionGroupPermissionPatch.apply(user.getConnectionGroupPermissions());
sharingProfilePermissionPatch.apply(user.getSharingProfilePermissions());
activeConnectionPermissionPatch.apply(user.getActiveConnectionPermissions());
userPermissionPatch.apply(user.getUserPermissions());
systemPermissionPatch.apply(user.getSystemPermissions());

View File

@@ -31,6 +31,7 @@ import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSession;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.rest.tunnel.TunnelCollectionResource;
import org.apache.guacamole.rest.tunnel.TunnelCollectionResourceFactory;
/**
* A REST resource which exposes all data associated with a Guacamole user's
@@ -54,6 +55,13 @@ public class SessionResource {
@Inject
private UserContextResourceFactory userContextResourceFactory;
/**
* Factory for creating instances of resources which represent the
* collection of tunnels within a GuacamoleSession.
*/
@Inject
private TunnelCollectionResourceFactory tunnelCollectionResourceFactory;
/**
* Creates a new SessionResource which exposes the data within the given
* GuacamoleSession.
@@ -105,7 +113,7 @@ public class SessionResource {
*/
@Path("tunnels")
public TunnelCollectionResource getTunnelCollectionResource() {
return new TunnelCollectionResource(session);
return tunnelCollectionResourceFactory.create(session);
}
}

View File

@@ -32,6 +32,7 @@ import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.ActiveConnection;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup;
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.rest.activeconnection.APIActiveConnection;
@@ -39,6 +40,7 @@ import org.apache.guacamole.rest.connection.APIConnection;
import org.apache.guacamole.rest.connectiongroup.APIConnectionGroup;
import org.apache.guacamole.rest.history.HistoryResource;
import org.apache.guacamole.rest.schema.SchemaResource;
import org.apache.guacamole.rest.sharingprofile.APISharingProfile;
import org.apache.guacamole.rest.user.APIUser;
/**
@@ -79,6 +81,14 @@ public class UserContextResource {
private DirectoryResourceFactory<ConnectionGroup, APIConnectionGroup>
connectionGroupDirectoryResourceFactory;
/**
* Factory for creating DirectoryResources which expose a given
* SharingProfile Directory.
*/
@Inject
private DirectoryResourceFactory<SharingProfile, APISharingProfile>
sharingProfileDirectoryResourceFactory;
/**
* Factory for creating DirectoryResources which expose a given
* User Directory.
@@ -153,6 +163,24 @@ public class UserContextResource {
userContext.getConnectionGroupDirectory());
}
/**
* Returns a new resource which represents the SharingProfile Directory
* contained within the UserContext exposed by this UserContextResource.
*
* @return
* A new resource which represents the SharingProfile Directory
* contained within the UserContext exposed by this UserContextResource.
*
* @throws GuacamoleException
* If an error occurs while retrieving the SharingProfile Directory.
*/
@Path("sharingProfiles")
public DirectoryResource<SharingProfile, APISharingProfile>
getSharingProfileDirectoryResource() throws GuacamoleException {
return sharingProfileDirectoryResourceFactory.create(userContext,
userContext.getSharingProfileDirectory());
}
/**
* Returns a new resource which represents the User Directory contained
* within the UserContext exposed by this UserContextResource.

View File

@@ -0,0 +1,207 @@
/*
* 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.rest.sharingprofile;
import java.util.Map;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.apache.guacamole.net.auth.SharingProfile;
/**
* The external representation used by the REST API for sharing profiles.
*
* @author Michael Jumper
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class APISharingProfile {
/**
* The human-readable name of this sharing profile.
*/
private String name;
/**
* The unique string which identifies this sharing profile within its
* containing directory.
*/
private String identifier;
/**
* The identifier of the primary connection that this sharing profile
* can be used to share.
*/
private String primaryConnectionIdentifier;
/**
* Map of all associated connection parameter values which apply when the
* sharing profile is used, indexed by parameter name.
*/
private Map<String, String> parameters;
/**
* Map of all associated attributes by attribute identifier.
*/
private Map<String, String> attributes;
/**
* Creates an empty, uninitialized APISharingProfile. The properties of the
* created APISharingProfile will need to be set individually as necessary
* via their corresponding setters.
*/
public APISharingProfile() {}
/**
* Creates a new APISharingProfile with its data populated from that of an
* existing SharingProfile. As the connection parameters of the
* SharingProfile are potentially sensitive, they will not be included in
* the new APISharingProfile.
*
* @param sharingProfile
* The sharing profile to use to populate the data of the new
* APISharingProfile.
*/
public APISharingProfile(SharingProfile sharingProfile) {
// Set main information
this.name = sharingProfile.getName();
this.identifier = sharingProfile.getIdentifier();
this.primaryConnectionIdentifier = sharingProfile.getPrimaryConnectionIdentifier();
// Associate any attributes
this.attributes = sharingProfile.getAttributes();
}
/**
* Returns the human-readable name of this sharing profile.
*
* @return
* The human-readable name of this sharing profile.
*/
public String getName() {
return name;
}
/**
* Set the human-readable name of this sharing profile.
*
* @param name
* The human-readable name of this sharing profile.
*/
public void setName(String name) {
this.name = name;
}
/**
* Returns the unique string which identifies this sharing profile within
* its containing directory.
*
* @return
* The unique string which identifies this sharing profile within its
* containing directory.
*/
public String getIdentifier() {
return identifier;
}
/**
* Sets the unique string which identifies this sharing profile within
* its containing directory.
*
* @param identifier
* The unique string which identifies this sharing profile within its
* containing directory.
*/
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
/**
* Returns the identifier of the primary connection that this sharing
* profile can be used to share.
*
* @return
* The identifier of the primary connection that this sharing profile
* can be used to share.
*/
public String getPrimaryConnectionIdentifier() {
return primaryConnectionIdentifier;
}
/**
* Sets the identifier of the primary connection that this sharing profile
* can be used to share.
*
* @param primaryConnectionIdentifier
* The identifier of the primary connection that this sharing profile
* can be used to share.
*/
public void setPrimaryConnectionIdentifier(String primaryConnectionIdentifier) {
this.primaryConnectionIdentifier = primaryConnectionIdentifier;
}
/**
* Returns a map of all associated connection parameter values which apply
* when the sharing profile is used, indexed by parameter name.
*
* @return
* A map of all associated connection parameter values which apply when
* the sharing profile is used, indexed by parameter name.
*/
public Map<String, String> getParameters() {
return parameters;
}
/**
* Sets the map of all associated connection parameter values which apply
* when the sharing profile is used, indexed by parameter name.
*
* @param parameters
* The map of all associated connection parameter values which apply
* when the sharing profile is used, indexed by parameter name.
*/
public void setParameters(Map<String, String> parameters) {
this.parameters = parameters;
}
/**
* Returns a map of all attributes associated with this sharing profile.
* Each entry key is the attribute identifier, while each value is the
* attribute value itself.
*
* @return
* The attribute map for this sharing profile.
*/
public Map<String, String> getAttributes() {
return attributes;
}
/**
* Sets the map of all attributes associated with this sharing profile. Each
* entry key is the attribute identifier, while each value is the attribute
* value itself.
*
* @param attributes
* The attribute map for this sharing profile.
*/
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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.rest.sharingprofile;
import java.util.Map;
import org.apache.guacamole.net.auth.SharingProfile;
/**
* Wrapper for APISharingProfile which provides a SharingProfile interface.
* Changes to the underlying APISharingProfile are reflected immediately in the
* values exposed by the SharingProfile interface, and changes made through the
* SharingProfile interface immediately affect the underlying APISharingProfile.
*
* @author Michael Jumper
*/
public class APISharingProfileWrapper implements SharingProfile {
/**
* The wrapped APISharingProfile.
*/
private final APISharingProfile apiSharingProfile;
/**
* Creates a new APISharingProfileWrapper which is backed by the given
* APISharingProfile.
*
* @param apiSharingProfile
* The APISharingProfile to wrap.
*/
public APISharingProfileWrapper(APISharingProfile apiSharingProfile) {
this.apiSharingProfile = apiSharingProfile;
}
@Override
public String getName() {
return apiSharingProfile.getName();
}
@Override
public void setName(String name) {
apiSharingProfile.setName(name);
}
@Override
public String getIdentifier() {
return apiSharingProfile.getIdentifier();
}
@Override
public void setIdentifier(String identifier) {
apiSharingProfile.setIdentifier(identifier);
}
@Override
public String getPrimaryConnectionIdentifier() {
return apiSharingProfile.getPrimaryConnectionIdentifier();
}
@Override
public void setPrimaryConnectionIdentifier(String primaryConnectionIdentifier) {
apiSharingProfile.setPrimaryConnectionIdentifier(primaryConnectionIdentifier);
}
@Override
public Map<String, String> getParameters() {
return apiSharingProfile.getParameters();
}
@Override
public void setParameters(Map<String, String> parameters) {
apiSharingProfile.setParameters(parameters);
}
@Override
public Map<String, String> getAttributes() {
return apiSharingProfile.getAttributes();
}
@Override
public void setAttributes(Map<String, String> attributes) {
apiSharingProfile.setAttributes(attributes);
}
}

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.rest.sharingprofile;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.SharingProfile;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
import org.apache.guacamole.rest.directory.DirectoryResource;
/**
* A REST resource which abstracts the operations available on a Directory of
* SharingProfiles.
*
* @author Michael Jumper
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class SharingProfileDirectoryResource
extends DirectoryResource<SharingProfile, APISharingProfile> {
/**
* Creates a new SharingProfileDirectoryResource which exposes the
* operations and subresources available for the given SharingProfile
* Directory.
*
* @param userContext
* The UserContext associated with the given Directory.
*
* @param directory
* The Directory being exposed.
*
* @param translator
* A DirectoryObjectTranslator implementation which handles
* SharingProfiles.
*
* @param resourceFactory
* A factory which can be used to create instances of resources
* representing SharingProfiles.
*/
@AssistedInject
public SharingProfileDirectoryResource(@Assisted UserContext userContext,
@Assisted Directory<SharingProfile> directory,
DirectoryObjectTranslator<SharingProfile, APISharingProfile> translator,
DirectoryObjectResourceFactory<SharingProfile, APISharingProfile> resourceFactory) {
super(userContext, directory, translator, resourceFactory);
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.rest.sharingprofile;
import com.google.inject.AbstractModule;
import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory;
import org.apache.guacamole.rest.directory.DirectoryObjectResource;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import org.apache.guacamole.net.auth.SharingProfile;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
import org.apache.guacamole.rest.directory.DirectoryResource;
import org.apache.guacamole.rest.directory.DirectoryResourceFactory;
/**
* Guice Module which configures injections required for handling SharingProfile
* resources via the REST API.
*
* @author Michael Jumper
*/
public class SharingProfileModule extends AbstractModule {
@Override
protected void configure() {
// Create the required DirectoryResourceFactory implementation
install(new FactoryModuleBuilder()
.implement(
new TypeLiteral<DirectoryResource<SharingProfile, APISharingProfile>>() {},
SharingProfileDirectoryResource.class
)
.build(new TypeLiteral<DirectoryResourceFactory<SharingProfile, APISharingProfile>>() {}));
// Create the required DirectoryObjectResourceFactory implementation
install(new FactoryModuleBuilder()
.implement(
new TypeLiteral<DirectoryObjectResource<SharingProfile, APISharingProfile>>() {},
SharingProfileResource.class
)
.build(new TypeLiteral<DirectoryObjectResourceFactory<SharingProfile, APISharingProfile>>() {}));
// Bind translator for converting between SharingProfile and APISharingProfile
bind(new TypeLiteral<DirectoryObjectTranslator<SharingProfile, APISharingProfile>>() {})
.to(SharingProfileObjectTranslator.class);
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.rest.sharingprofile;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.SharingProfile;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
/**
* Translator which converts between SharingProfile objects and
* APISharingProfile objects.
*
* @author Michael Jumper
*/
public class SharingProfileObjectTranslator
implements DirectoryObjectTranslator<SharingProfile, APISharingProfile> {
@Override
public APISharingProfile toExternalObject(SharingProfile object)
throws GuacamoleException {
return new APISharingProfile(object);
}
@Override
public SharingProfile toInternalObject(APISharingProfile object) {
return new APISharingProfileWrapper(object);
}
@Override
public void applyExternalChanges(SharingProfile existingObject,
APISharingProfile object) {
// Update the sharing profile
existingObject.setPrimaryConnectionIdentifier(object.getPrimaryConnectionIdentifier());
existingObject.setName(object.getName());
existingObject.setParameters(object.getParameters());
existingObject.setAttributes(object.getAttributes());
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.rest.sharingprofile;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
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.permission.ObjectPermission;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
import org.apache.guacamole.net.auth.permission.SystemPermission;
import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
import org.apache.guacamole.rest.directory.DirectoryObjectResource;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
/**
* A REST resource which abstracts the operations available on an existing
* SharingProfile.
*
* @author Michael Jumper
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class SharingProfileResource
extends DirectoryObjectResource<SharingProfile, APISharingProfile> {
/**
* The UserContext associated with the Directory which contains the
* SharingProfile exposed by this resource.
*/
private final UserContext userContext;
/**
* The SharingProfile object represented by this SharingProfileResource.
*/
private final SharingProfile sharingProfile;
/**
* Creates a new SharingProfileResource which exposes the operations and
* subresources available for the given SharingProfile.
*
* @param userContext
* The UserContext associated with the given Directory.
*
* @param directory
* The Directory which contains the given SharingProfile.
*
* @param sharingProfile
* The SharingProfile that this SharingProfileResource should represent.
*
* @param translator
* A DirectoryObjectTranslator implementation which handles the type of
* object given.
*/
@AssistedInject
public SharingProfileResource(@Assisted UserContext userContext,
@Assisted Directory<SharingProfile> directory,
@Assisted SharingProfile sharingProfile,
DirectoryObjectTranslator<SharingProfile, APISharingProfile> translator) {
super(directory, sharingProfile, translator);
this.userContext = userContext;
this.sharingProfile = sharingProfile;
}
/**
* Retrieves the connection parameters associated with the SharingProfile
* exposed by this SharingProfile resource.
*
* @return
* A map of parameter name/value pairs.
*
* @throws GuacamoleException
* If an error occurs while retrieving the connection parameters of the
* SharingProfile.
*/
@GET
@Path("parameters")
public Map<String, String> getParameters()
throws GuacamoleException {
User self = userContext.self();
// Retrieve permission sets
SystemPermissionSet systemPermissions = self.getSystemPermissions();
ObjectPermissionSet sharingProfilePermissions = self.getSharingProfilePermissions();
// Deny access if adminstrative or update permission is missing
String identifier = sharingProfile.getIdentifier();
if (!systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER)
&& !sharingProfilePermissions.hasPermission(ObjectPermission.Type.UPDATE, identifier))
throw new GuacamoleSecurityException("Permission to read sharing profile parameters denied.");
// Return parameter map
return sharingProfile.getParameters();
}
}

View File

@@ -0,0 +1,25 @@
/*
* 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.
*/
/**
* Classes related to retrieving or manipulating sharing profiles using the
* Guacamole REST API.
*/
package org.apache.guacamole.rest.sharingprofile;

View File

@@ -19,6 +19,9 @@
package org.apache.guacamole.rest.tunnel;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.Consumes;
@@ -30,7 +33,7 @@ import javax.ws.rs.core.MediaType;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceNotFoundException;
import org.apache.guacamole.GuacamoleSession;
import org.apache.guacamole.tunnel.StreamInterceptingTunnel;
import org.apache.guacamole.tunnel.UserTunnel;
/**
* A REST resource which exposes the active tunnels of a Guacamole session.
@@ -46,6 +49,12 @@ public class TunnelCollectionResource {
*/
private final GuacamoleSession session;
/**
* Factory for creating instances of resources which represent tunnels.
*/
@Inject
private TunnelResourceFactory tunnelResourceFactory;
/**
* Creates a new TunnelCollectionResource which exposes the active tunnels
* of the given GuacamoleSession.
@@ -54,7 +63,8 @@ public class TunnelCollectionResource {
* The GuacamoleSession whose tunnels should be exposed by this
* resource.
*/
public TunnelCollectionResource(GuacamoleSession session) {
@AssistedInject
public TunnelCollectionResource(@Assisted GuacamoleSession session) {
this.session = session;
}
@@ -89,15 +99,15 @@ public class TunnelCollectionResource {
public TunnelResource getTunnel(@PathParam("tunnel") String tunnelUUID)
throws GuacamoleException {
Map<String, StreamInterceptingTunnel> tunnels = session.getTunnels();
Map<String, UserTunnel> tunnels = session.getTunnels();
// Pull tunnel with given UUID
final StreamInterceptingTunnel tunnel = tunnels.get(tunnelUUID);
final UserTunnel tunnel = tunnels.get(tunnelUUID);
if (tunnel == null)
throw new GuacamoleResourceNotFoundException("No such tunnel.");
// Return corresponding tunnel resource
return new TunnelResource(tunnel);
return tunnelResourceFactory.create(tunnel);
}

View File

@@ -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.rest.tunnel;
import org.apache.guacamole.GuacamoleSession;
/**
* Factory which creates resources that expose the collection of tunnels
* contained within a given GuacamoleSession.
*
* @author Michael Jumper
*/
public interface TunnelCollectionResourceFactory {
/**
* Creates a new TunnelCollectionResource which exposes the collection of
* tunnels stored within the given GuacamoleSession.
*
* @param session
* The GuacamoleSession whose collection of tunnels should be exposed.
*
* @return
* A new TunnelCollectionResource which exposes the collection of
* tunnels stored within the given GuacamoleSession.
*/
TunnelCollectionResource create(GuacamoleSession session);
}

View File

@@ -19,6 +19,9 @@
package org.apache.guacamole.rest.tunnel;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.Path;
@@ -27,7 +30,12 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.tunnel.StreamInterceptingTunnel;
import org.apache.guacamole.net.auth.ActiveConnection;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.rest.activeconnection.APIActiveConnection;
import org.apache.guacamole.rest.directory.DirectoryObjectResource;
import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory;
import org.apache.guacamole.tunnel.UserTunnel;
/**
* A REST resource which abstracts the operations available for an individual
@@ -48,7 +56,15 @@ public class TunnelResource {
/**
* The tunnel that this TunnelResource represents.
*/
private final StreamInterceptingTunnel tunnel;
private final UserTunnel tunnel;
/**
* A factory which can be used to create instances of resources representing
* ActiveConnections.
*/
@Inject
private DirectoryObjectResourceFactory<ActiveConnection, APIActiveConnection>
activeConnectionResourceFactory;
/**
* Creates a new TunnelResource which exposes the operations and
@@ -57,10 +73,36 @@ public class TunnelResource {
* @param tunnel
* The tunnel that this TunnelResource should represent.
*/
public TunnelResource(StreamInterceptingTunnel tunnel) {
@AssistedInject
public TunnelResource(@Assisted UserTunnel tunnel) {
this.tunnel = tunnel;
}
/**
* Retrieves a resource representing the ActiveConnection object associated
* with this tunnel.
*
* @return
* A resource representing the ActiveConnection object associated with
* this tunnel.
*
* @throws GuacamoleException
* If an error occurs while retrieving the ActiveConnection.
*/
@Path("activeConnection")
public DirectoryObjectResource<ActiveConnection, APIActiveConnection>
getActiveConnection() throws GuacamoleException {
// Pull the UserContext from the tunnel
UserContext userContext = tunnel.getUserContext();
// Return the associated ActiveConnection as a resource
return activeConnectionResourceFactory.create(userContext,
userContext.getActiveConnectionDirectory(),
tunnel.getActiveConnection());
}
/**
* Intercepts and returns the entire contents of a specific stream.
*

View File

@@ -0,0 +1,44 @@
/*
* 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.rest.tunnel;
import org.apache.guacamole.tunnel.UserTunnel;
/**
* Factory which creates resources that expose the contents of a given
* tunnel.
*
* @author Michael Jumper
*/
public interface TunnelResourceFactory {
/**
* Creates a new TunnelResource which exposes the contents of the
* given tunnel.
*
* @param tunnel
* The tunnel whose contents should be exposed.
*
* @return
* A new TunnelResource which exposes the contents of the given tunnel.
*/
TunnelResource create(UserTunnel tunnel);
}

View File

@@ -97,6 +97,11 @@ public class APIUserWrapper implements User {
throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access.");
}
@Override
public ObjectPermissionSet getSharingProfilePermissions() throws GuacamoleException {
throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access.");
}
@Override
public ObjectPermissionSet getUserPermissions()
throws GuacamoleException {

View File

@@ -211,6 +211,10 @@ public class TunnelRequestService {
* @param session
* The Guacamole session to associate the tunnel with.
*
* @param context
* The UserContext associated with the user for whom the tunnel is
* being created.
*
* @param type
* The type of object being connected to (connection or group).
*
@@ -226,12 +230,12 @@ public class TunnelRequestService {
* If an error occurs while obtaining the tunnel.
*/
protected GuacamoleTunnel createAssociatedTunnel(GuacamoleTunnel tunnel,
final String authToken, final GuacamoleSession session,
final TunnelRequest.Type type, final String id)
throws GuacamoleException {
final String authToken, final GuacamoleSession session,
final UserContext context, final TunnelRequest.Type type,
final String id) throws GuacamoleException {
// Monitor tunnel closure and data
StreamInterceptingTunnel monitoredTunnel = new StreamInterceptingTunnel(tunnel) {
UserTunnel monitoredTunnel = new UserTunnel(context, tunnel) {
/**
* The time the connection began, measured in milliseconds since
@@ -328,7 +332,7 @@ public class TunnelRequestService {
GuacamoleTunnel tunnel = createConnectedTunnel(userContext, type, id, info);
// Associate tunnel with session
return createAssociatedTunnel(tunnel, authToken, session, type, id);
return createAssociatedTunnel(tunnel, authToken, session, userContext, type, id);
}

View File

@@ -0,0 +1,120 @@
/*
* 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.tunnel;
import java.util.Collection;
import java.util.UUID;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.ActiveConnection;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.UserContext;
/**
* Tunnel implementation which associates a given tunnel with the UserContext of
* the user that created it.
*
* @author Michael Jumper
*/
public class UserTunnel extends StreamInterceptingTunnel {
/**
* The UserContext associated with the user for whom this tunnel was
* created. This UserContext MUST be from the AuthenticationProvider that
* created this tunnel.
*/
private final UserContext userContext;
/**
* Creates a new UserTunnel which wraps the given tunnel, associating it
* with the given UserContext. The UserContext MUST be from the
* AuthenticationProvider that created this tunnel, and MUST be associated
* with the user for whom this tunnel was created.
*
* @param userContext
* The UserContext associated with the user for whom this tunnel was
* created. This UserContext MUST be from the AuthenticationProvider
* that created this tunnel.
*
* @param tunnel
* The tunnel whose stream-related instruction should be intercepted if
* interceptStream() is invoked.
*/
public UserTunnel(UserContext userContext, GuacamoleTunnel tunnel) {
super(tunnel);
this.userContext = userContext;
}
/**
* Returns the UserContext of the user for whom this tunnel was created.
* This UserContext will be the UserContext from the AuthenticationProvider
* that created this tunnel.
*
* @return
* The UserContext of the user for whom this tunnel was created.
*/
public UserContext getUserContext() {
return userContext;
}
/**
* Returns the ActiveConnection object associated with this tunnel within
* the AuthenticationProvider and UserContext which created the tunnel. If
* the AuthenticationProvider is not tracking active connections, or this
* tunnel is no longer active, this will be null.
*
* @return
* The ActiveConnection object associated with this tunnel, or null if
* this tunnel is no longer active or the AuthenticationProvider which
* created the tunnel is not tracking active connections.
*
* @throws GuacamoleException
* If an error occurs which prevents retrieval of the user's current
* active connections.
*/
public ActiveConnection getActiveConnection() throws GuacamoleException {
// Pull the UUID of the current tunnel
UUID uuid = getUUID();
// Get the directory of active connections
Directory<ActiveConnection> activeConnectionDirectory = userContext.getActiveConnectionDirectory();
Collection<String> activeConnectionIdentifiers = activeConnectionDirectory.getIdentifiers();
// Search all connections for a tunnel which matches this tunnel
for (ActiveConnection activeConnection : activeConnectionDirectory.getAll(activeConnectionIdentifiers)) {
// If we lack access, continue with next tunnel
GuacamoleTunnel tunnel = activeConnection.getTunnel();
if (tunnel == null)
continue;
// Tunnels are equivalent if they have the same UUID
if (uuid.equals(tunnel.getUUID()))
return activeConnection;
}
// No active connection associated with this tunnel
return null;
}
}

View File

@@ -166,6 +166,41 @@ angular.module('rest').factory('activeConnectionService', ['$injector',
};
/**
* Makes a request to the REST API to generate credentials which have
* access strictly to the given active connection, using the restrictions
* defined by the given sharing profile, returning a promise that provides
* the resulting @link{UserCredentials} object if successful.
*
* @param {String} id
* The identifier of the active connection being shared.
*
* @param {String} sharingProfile
* The identifier of the sharing profile dictating the
* semantics/restrictions which apply to the shared session.
*
* @returns {Promise.<UserCredentials>}
* A promise which will resolve with a @link{UserCredentials} object
* upon success.
*/
service.getSharingCredentials = function getSharingCredentials(dataSource, id, sharingProfile) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Generate sharing credentials
return $http({
method : 'GET',
url : 'api/session/data/' + encodeURIComponent(dataSource)
+ '/activeConnections/' + encodeURIComponent(id)
+ '/sharingCredentials/' + encodeURIComponent(sharingProfile),
params : httpParameters
});
};
return service;
}]);

View File

@@ -68,6 +68,73 @@ angular.module('rest').factory('tunnelService', ['$injector',
};
/**
* Retrieves the set of sharing profiles that the current user can use to
* share the active connection of the given tunnel.
*
* @param {String} tunnel
* The UUID of the tunnel associated with the Guacamole connection
* whose sharing profiles are being retrieved.
*
* @returns {Promise.<Object.<String, SharingProfile>>}
* A promise which will resolve with a map of @link{SharingProfile}
* objects where each key is the identifier of the corresponding
* sharing profile.
*/
service.getSharingProfiles = function getSharingProfiles(tunnel) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Retrieve all associated sharing profiles
return $http({
method : 'GET',
url : 'api/session/tunnels/' + encodeURIComponent(tunnel)
+ '/activeConnection/connection/sharingProfiles',
params : httpParameters
});
};
/**
* Makes a request to the REST API to generate credentials which have
* access strictly to the active connection associated with the given
* tunnel, using the restrictions defined by the given sharing profile,
* returning a promise that provides the resulting @link{UserCredentials}
* object if successful.
*
* @param {String} tunnel
* The UUID of the tunnel associated with the Guacamole connection
* being shared.
*
* @param {String} sharingProfile
* The identifier of the connection object dictating the
* semantics/restrictions which apply to the shared session.
*
* @returns {Promise.<UserCredentials>}
* A promise which will resolve with a @link{UserCredentials} object
* upon success.
*/
service.getSharingCredentials = function getSharingCredentials(tunnel, sharingProfile) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Generate sharing credentials
return $http({
method : 'GET',
url : 'api/session/tunnels/' + encodeURIComponent(tunnel)
+ '/activeConnection/sharingCredentials/'
+ encodeURIComponent(sharingProfile),
params : httpParameters
});
};
/**
* Makes a request to the REST API to retrieve the contents of a stream
* which has been created within the active Guacamole connection associated

View File

@@ -73,7 +73,7 @@ angular.module('rest').factory('Field', [function defineField() {
*
* @type String
*/
TEXT : "TEXT",
TEXT : 'TEXT',
/**
* The type string associated with parameters that may contain an
@@ -82,7 +82,7 @@ angular.module('rest').factory('Field', [function defineField() {
*
* @type String
*/
USERNAME : "USERNAME",
USERNAME : 'USERNAME',
/**
* The type string associated with parameters that may contain an
@@ -91,7 +91,7 @@ angular.module('rest').factory('Field', [function defineField() {
*
* @type String
*/
PASSWORD : "PASSWORD",
PASSWORD : 'PASSWORD',
/**
* The type string associated with parameters that may contain only
@@ -99,7 +99,7 @@ angular.module('rest').factory('Field', [function defineField() {
*
* @type String
*/
NUMERIC : "NUMERIC",
NUMERIC : 'NUMERIC',
/**
* The type string associated with parameters that may contain only a
@@ -110,7 +110,7 @@ angular.module('rest').factory('Field', [function defineField() {
*
* @type String
*/
BOOLEAN : "BOOLEAN",
BOOLEAN : 'BOOLEAN',
/**
* The type string associated with parameters that may contain a
@@ -118,7 +118,7 @@ angular.module('rest').factory('Field', [function defineField() {
*
* @type String
*/
ENUM : "ENUM",
ENUM : 'ENUM',
/**
* The type string associated with parameters that may contain any
@@ -126,7 +126,7 @@ angular.module('rest').factory('Field', [function defineField() {
*
* @type String
*/
MULTILINE : "MULTILINE",
MULTILINE : 'MULTILINE',
/**
* The type string associated with parameters that may contain timezone
@@ -135,7 +135,7 @@ angular.module('rest').factory('Field', [function defineField() {
*
* @type String
*/
TIMEZONE : "TIMEZONE",
TIMEZONE : 'TIMEZONE',
/**
* The type string associated with parameters that may contain dates.
@@ -143,7 +143,7 @@ angular.module('rest').factory('Field', [function defineField() {
*
* @type String
*/
DATE : "DATE",
DATE : 'DATE',
/**
* The type string associated with parameters that may contain times.
@@ -152,7 +152,15 @@ angular.module('rest').factory('Field', [function defineField() {
*
* @type String
*/
TIME : "TIME"
TIME : 'TIME',
/**
* An HTTP query parameter which is expected to be embedded in the URL
* given to a user.
*
* @type String
*/
QUERY_PARAMETER : 'QUERY_PARAMETER'
};

View File

@@ -0,0 +1,85 @@
/*
* 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.
*/
/**
* Service which defines the SharingProfile class.
*/
angular.module('rest').factory('SharingProfile', [function defineSharingProfile() {
/**
* The object returned by REST API calls when representing the data
* associated with a sharing profile.
*
* @constructor
* @param {SharingProfile|Object} [template={}]
* The object whose properties should be copied within the new
* SharingProfile.
*/
var SharingProfile = function SharingProfile(template) {
// Use empty object by default
template = template || {};
/**
* The unique identifier associated with this sharing profile.
*
* @type String
*/
this.identifier = template.identifier;
/**
* The unique identifier of the connection that this sharing profile
* can be used to share.
*
* @type String
*/
this.primaryConnectionIdentifier = template.primaryConnectionIdentifier;
/**
* The human-readable name of this sharing profile, which is not
* necessarily unique.
*
* @type String
*/
this.name = template.name;
/**
* Connection configuration parameters, as dictated by the protocol in
* use by the primary connection, arranged as name/value pairs. This
* information may not be available until directly queried. If this
* information is unavailable, this property will be null or undefined.
*
* @type Object.<String, String>
*/
this.parameters = template.parameters;
/**
* Arbitrary name/value pairs which further describe this sharing
* profile. The semantics and validity of these attributes are dictated
* by the extension which defines them.
*
* @type Object.<String, String>
*/
this.attributes = {};
};
return SharingProfile;
}]);

View File

@@ -0,0 +1,133 @@
/*
* 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.
*/
/**
* Service which defines the UserCredentials class.
*/
angular.module('rest').factory('UserCredentials', ['$injector', function defineUserCredentials($injector) {
// Required services
var $window = $injector.get('$window');
// Required types
var Field = $injector.get('Field');
/**
* The object returned by REST API calls to define a full set of valid
* credentials, including field definitions and corresponding expected
* values.
*
* @constructor
* @param {UserCredentials|Object} [template={}]
* The object whose properties should be copied within the new
* UserCredentials.
*/
var UserCredentials = function UserCredentials(template) {
// Use empty object by default
template = template || {};
/**
* Any parameters which should be provided when these credentials are
* submitted. If no such information is available, this will be null.
*
* @type Field[]
*/
this.expected = template.expected;
/**
* A map of all field values by field name. The fields having the names
* used within this map should be defined within the @link{Field} array
* stored under the @link{expected} property.
*
* @type Object.<String, String>
*/
this.values = template.values;
};
/**
* Generates a query string containing all QUERY_PARAMETER fields from the
* given UserCredentials object, along with their corresponding values. The
* parameter names and values will be appropriately URL-encoded and
* separated by ampersands.
*
* @param {UserCredentials} userCredentials
* The UserCredentials to retrieve all query parameters from.
*
* @returns {String}
* A string containing all QUERY_PARAMETER fields as name/value pairs
* separated by ampersands, where each name is separated by the value
* by an equals sign.
*/
UserCredentials.getQueryParameters = function getQueryParameters(userCredentials) {
// Build list of parameter name/value pairs
var parameters = [];
angular.forEach(userCredentials.expected, function addQueryParameter(field) {
// Only add query parameters
if (field.type !== Field.Type.QUERY_PARAMETER)
return;
// Pull parameter name and value
var name = field.name;
var value = userCredentials.values[name];
// Properly encode name/value pair
parameters.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));
});
// Separate each name/value pair by an ampersand
return parameters.join('&');
};
/**
* Returns a fully-qualified, absolute URL to Guacamole prepopulated with
* any query parameters dictated by the QUERY_PARAMETER fields defined in
* the given UserCredentials.
*
* @param {UserCredentials} userCredentials
* The UserCredentials to retrieve all query parameters from.
*
* @returns {String}
* A fully-qualified, absolute URL to Guacamole prepopulated with the
* query parameters dictated by the given UserCredentials.
*/
UserCredentials.getLink = function getLink(userCredentials) {
// Build base link
var link = $window.location.origin
+ $window.location.pathname
+ '#/';
// Add any required parameters
var params = UserCredentials.getQueryParameters(userCredentials);
if (params)
link += '?' + params;
return link;
};
return UserCredentials;
}]);