From eededc33caea697e07587bbded77f40649283159 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 14 Jul 2016 01:47:06 -0700 Subject: [PATCH 01/18] GUACAMOLE-5: Define sharing profiles and their relationship to connections. --- .../net/auth/AbstractConnection.java | 9 ++ .../net/auth/AbstractSharingProfile.java | 119 ++++++++++++++++++ .../apache/guacamole/net/auth/Connection.java | 18 +++ .../guacamole/net/auth/ConnectionRecord.java | 26 ++++ .../guacamole/net/auth/SharingProfile.java | 119 ++++++++++++++++++ .../guacamole/net/auth/UserContext.java | 26 ++++ .../net/auth/simple/SimpleUserContext.java | 12 ++ .../rest/connection/APIConnectionWrapper.java | 6 + 8 files changed, 335 insertions(+) create mode 100644 guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractSharingProfile.java create mode 100644 guacamole-ext/src/main/java/org/apache/guacamole/net/auth/SharingProfile.java diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractConnection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractConnection.java index e3c9af54e..3a751367c 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractConnection.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractConnection.java @@ -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 getSharingProfileIdentifiers() + throws GuacamoleException { + return Collections.emptySet(); + } + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractSharingProfile.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractSharingProfile.java new file mode 100644 index 000000000..0a664f2f3 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractSharingProfile.java @@ -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 parameters = new HashMap(); + + @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 getParameters() { + return parameters; + } + + @Override + public void setParameters(Map 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); + + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connection.java index dae10702b..99930002d 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connection.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connection.java @@ -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 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 getSharingProfileIdentifiers() throws GuacamoleException; + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ConnectionRecord.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ConnectionRecord.java index 85ce95a47..b6b0c06f9 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ConnectionRecord.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ConnectionRecord.java @@ -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. * diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/SharingProfile.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/SharingProfile.java new file mode 100644 index 000000000..9c12a1543 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/SharingProfile.java @@ -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 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 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 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 attributes); + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java index 62eab4590..0226ae14e 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java @@ -106,6 +106,21 @@ public interface UserContext { Directory 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 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
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 getSharingProfileAttributes(); + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java index 24cbcc3a4..28bb6b1a6 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUserContext.java @@ -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 getSharingProfileDirectory() + throws GuacamoleException { + return new SimpleDirectory(); + } + @Override public Directory getActiveConnectionDirectory() throws GuacamoleException { @@ -219,4 +226,9 @@ public class SimpleUserContext implements UserContext { return Collections.emptyList(); } + @Override + public Collection getSharingProfileAttributes() { + return Collections.emptyList(); + } + } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnectionWrapper.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnectionWrapper.java index 862c30aa4..1153d7231 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnectionWrapper.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnectionWrapper.java @@ -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 getSharingProfileIdentifiers() { + throw new UnsupportedOperationException("Operation not supported."); + } + @Override public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { throw new UnsupportedOperationException("Operation not supported."); From a2ad7a69abe58e39e376370e8e6fdc017629cd6e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 15 Jul 2016 12:49:25 -0700 Subject: [PATCH 02/18] GUACAMOLE-5: Add sharing profile permissions. --- .../java/org/apache/guacamole/net/auth/User.java | 14 ++++++++++++++ .../net/auth/permission/SystemPermission.java | 5 +++++ .../guacamole/net/auth/simple/SimpleUser.java | 5 +++++ 3 files changed, 24 insertions(+) diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java index d14cdd509..49a5e667f 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java @@ -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. diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermission.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermission.java index e33f8e0f7..33779a543 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermission.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/permission/SystemPermission.java @@ -49,6 +49,11 @@ public class SystemPermission implements Permission { */ 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, diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java index da169fc6c..0b7a86041 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleUser.java @@ -195,4 +195,9 @@ public class SimpleUser extends AbstractUser { return new SimpleObjectPermissionSet(); } + @Override + public ObjectPermissionSet getSharingProfilePermissions() { + return new SimpleObjectPermissionSet(); + } + } From 0f49c39ed4c3768e8886d50b7e3a52193039bbc2 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 15 Jul 2016 12:50:47 -0700 Subject: [PATCH 03/18] GUACAMOLE-5: Track the sharing profile associated with an active connection. --- .../net/auth/AbstractActiveConnection.java | 15 +++++++++++++++ .../guacamole/net/auth/ActiveConnection.java | 19 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractActiveConnection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractActiveConnection.java index 7b13bf4e1..9faacc03c 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractActiveConnection.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractActiveConnection.java @@ -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; diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActiveConnection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActiveConnection.java index 482b1eba3..54472a5ad 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActiveConnection.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActiveConnection.java @@ -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. * From ad50a7729343698e69ad983dc5cdb9b848ef03a5 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 20 Apr 2016 12:06:40 -0700 Subject: [PATCH 04/18] GUACAMOLE-5: Implement QUERY_PARAMETER field type. --- .../java/org/apache/guacamole/form/Field.java | 6 ++++ .../src/main/webapp/app/rest/types/Field.js | 28 ++++++++++++------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java b/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java index d89f9f728..8b306e947 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java @@ -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"; + } /** diff --git a/guacamole/src/main/webapp/app/rest/types/Field.js b/guacamole/src/main/webapp/app/rest/types/Field.js index e8589d5e1..5204268d3 100644 --- a/guacamole/src/main/webapp/app/rest/types/Field.js +++ b/guacamole/src/main/webapp/app/rest/types/Field.js @@ -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' }; From f9f78a64fc8b77219b2d996a25d6e238071bb65c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 20 Apr 2016 12:08:30 -0700 Subject: [PATCH 05/18] GUACAMOLE-5: Define UserCredentials object which couples a CredentialsInfo with defined parameter values. --- .../net/auth/credentials/UserCredentials.java | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 guacamole-ext/src/main/java/org/apache/guacamole/net/auth/credentials/UserCredentials.java diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/credentials/UserCredentials.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/credentials/UserCredentials.java new file mode 100644 index 000000000..e88706f1c --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/credentials/UserCredentials.java @@ -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 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 fields, Map 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 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()); + } + + /** + * 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 fields) { + this(fields, new HashMap()); + } + + /** + * 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 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 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()); + } + +} From e687ed040f5c0db82ac0f951decca25a65575aa6 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 20 Apr 2016 12:09:31 -0700 Subject: [PATCH 06/18] GUACAMOLE-5: Define Shareable interface which defines a mechanism for generating (likely temporary) credentials. --- .../apache/guacamole/net/auth/Shareable.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Shareable.java diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Shareable.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Shareable.java new file mode 100644 index 000000000..190f1a423 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Shareable.java @@ -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 + * The type of object which dictates the semantics/restrictions of shared + * objects. + * + * @author Michael Jumper + */ +public interface Shareable { + + /** + * 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}<{@link T}> of the same {@link UserContext} that + * this Shareable was retrieved from. + * + * @param identifier + * The identifier of a {@link T} within the + * {@link Directory}<{@link T}> 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; + +} From 0a0b5000bf5bf44b62ac64b03e10d4eb0bb9b51e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 15 Jul 2016 13:01:59 -0700 Subject: [PATCH 07/18] GUACAMOLE-5: ActiveConnections should be Shareable, to allow sharing links to be generated based on a SharingProfile. --- .../java/org/apache/guacamole/net/auth/ActiveConnection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActiveConnection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActiveConnection.java index 54472a5ad..6f3360063 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActiveConnection.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActiveConnection.java @@ -28,7 +28,7 @@ import org.apache.guacamole.net.GuacamoleTunnel; * * @author Michael Jumper */ -public interface ActiveConnection extends Identifiable { +public interface ActiveConnection extends Identifiable, Shareable { /** * Returns the identifier of the connection being actively used. Unlike the From 4eebc3b3015415431b76fce2bf5a29773c774d16 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 15 Jul 2016 13:25:39 -0700 Subject: [PATCH 08/18] GUACAMOLE-5: Bring LDAP authentication up to date with sharing profile API changes. --- .../apache/guacamole/auth/ldap/user/UserContext.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java index 2b705d2b8..62125178b 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/user/UserContext.java @@ -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(); } + @Override + public Directory getSharingProfileDirectory() + throws GuacamoleException { + return new SimpleDirectory(); + } + @Override public ConnectionRecordSet getConnectionHistory() throws GuacamoleException { @@ -214,4 +221,9 @@ public class UserContext implements org.apache.guacamole.net.auth.UserContext { return Collections.emptyList(); } + @Override + public Collection getSharingProfileAttributes() { + return Collections.emptyList(); + } + } From 2131abb4807b55949912af0af81605645fa9c655 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 15 Jul 2016 13:29:39 -0700 Subject: [PATCH 09/18] GUACAMOLE-5: Bring JDBC authentication up to date with sharing profile API changes. --- .../TrackedActiveConnection.java | 31 +++++++++++++++++-- .../jdbc/connection/ModeledConnection.java | 7 +++++ .../connection/ModeledConnectionRecord.java | 10 ++++++ .../jdbc/tunnel/ActiveConnectionRecord.java | 10 ++++++ .../guacamole/auth/jdbc/user/ModeledUser.java | 7 +++++ .../guacamole/auth/jdbc/user/UserContext.java | 14 +++++++++ 6 files changed, 76 insertions(+), 3 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java index c02e54d87..d4a23ffbc 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java @@ -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; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnection.java index caa9675d0..6cd30d4bb 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnection.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnection.java @@ -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 getSharingProfileIdentifiers() + throws GuacamoleException { + return Collections.emptySet(); + } + @Override public List getHistory() throws GuacamoleException { return connectionService.retrieveHistory(getCurrentUser(), this); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java index 4296b6d97..41a5fdb34 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java @@ -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(); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java index 45122cd61..a34e339c3 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java @@ -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; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java index 8b17523f7..26bdbcfc2 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java @@ -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 implements Us return connectionGroupPermissionService.getPermissionSet(getCurrentUser(), this); } + @Override + public ObjectPermissionSet getSharingProfilePermissions() + throws GuacamoleException { + return new SimpleObjectPermissionSet(); + } + @Override public ObjectPermissionSet getActiveConnectionPermissions() throws GuacamoleException { diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserContext.java index 8df1d3c2b..7cb52f07e 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserContext.java @@ -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 getSharingProfileDirectory() + throws GuacamoleException { + return new SimpleDirectory(); + } + @Override public Directory getActiveConnectionDirectory() throws GuacamoleException { @@ -173,4 +182,9 @@ public class UserContext extends RestrictedObject return ModeledConnectionGroup.ATTRIBUTES; } + @Override + public Collection getSharingProfileAttributes() { + return Collections.emptyList(); + } + } From c231f4eb571101e92afef27a929ed31d8da23040 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 15 Jul 2016 18:10:05 -0700 Subject: [PATCH 10/18] GUACAMOLE-5: Add SharingProfiles to REST API. --- .../guacamole/rest/RESTServiceModule.java | 2 + .../rest/history/APIConnectionRecord.java | 52 ++++- .../rest/permission/APIPermissionSet.java | 35 +++ .../permission/PermissionSetResource.java | 21 ++ .../rest/session/UserContextResource.java | 28 +++ .../sharingprofile/APISharingProfile.java | 207 ++++++++++++++++++ .../APISharingProfileWrapper.java | 101 +++++++++ .../SharingProfileDirectoryResource.java | 72 ++++++ .../sharingprofile/SharingProfileModule.java | 65 ++++++ .../SharingProfileObjectTranslator.java | 58 +++++ .../SharingProfileResource.java | 125 +++++++++++ .../rest/sharingprofile/package-info.java | 25 +++ .../guacamole/rest/user/APIUserWrapper.java | 5 + 13 files changed, 789 insertions(+), 7 deletions(-) create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/APISharingProfile.java create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/APISharingProfileWrapper.java create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileDirectoryResource.java create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileModule.java create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileObjectTranslator.java create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileResource.java create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/package-info.java diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java index 29782788a..7b7322b8e 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java @@ -39,6 +39,7 @@ 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.user.UserModule; /** @@ -96,6 +97,7 @@ public class RESTServiceModule extends ServletModule { install(new ActiveConnectionModule()); install(new ConnectionModule()); install(new ConnectionGroupModule()); + install(new SharingProfileModule()); install(new UserModule()); // Set up the servlet and JSON mappings diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecord.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecord.java index b1a796e16..36e639cf3 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecord.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecord.java @@ -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. * diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/permission/APIPermissionSet.java b/guacamole/src/main/java/org/apache/guacamole/rest/permission/APIPermissionSet.java index 7439401b7..5c15a2b9f 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/permission/APIPermissionSet.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/permission/APIPermissionSet.java @@ -53,6 +53,12 @@ public class APIPermissionSet { private Map> connectionGroupPermissions = new HashMap>(); + /** + * Map of sharing profile ID to the set of granted permissions. + */ + private Map> sharingProfilePermissions = + new HashMap>(); + /** * 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> 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> 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 diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java index e75460ccc..3ad005c42 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java @@ -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 connectionPermissionPatch = new PermissionSetPatch(); PermissionSetPatch connectionGroupPermissionPatch = new PermissionSetPatch(); + PermissionSetPatch sharingProfilePermissionPatch = new PermissionSetPatch(); PermissionSetPatch activeConnectionPermissionPatch = new PermissionSetPatch(); PermissionSetPatch userPermissionPatch = new PermissionSetPatch(); PermissionSetPatch systemPermissionPatch = new PermissionSetPatch(); @@ -205,6 +212,19 @@ public class PermissionSetResource { } + // Create sharing profile permission if path has sharing profileprefix + 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()); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java index e0c88555e..31bb002dc 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java @@ -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 connectionGroupDirectoryResourceFactory; + /** + * Factory for creating DirectoryResources which expose a given + * SharingProfile Directory. + */ + @Inject + private DirectoryResourceFactory + 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 + 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. diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/APISharingProfile.java b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/APISharingProfile.java new file mode 100644 index 000000000..822bcee7f --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/APISharingProfile.java @@ -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 parameters; + + /** + * Map of all associated attributes by attribute identifier. + */ + private Map 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 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 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 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 attributes) { + this.attributes = attributes; + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/APISharingProfileWrapper.java b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/APISharingProfileWrapper.java new file mode 100644 index 000000000..effb1d43a --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/APISharingProfileWrapper.java @@ -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 getParameters() { + return apiSharingProfile.getParameters(); + } + + @Override + public void setParameters(Map parameters) { + apiSharingProfile.setParameters(parameters); + } + + @Override + public Map getAttributes() { + return apiSharingProfile.getAttributes(); + } + + @Override + public void setAttributes(Map attributes) { + apiSharingProfile.setAttributes(attributes); + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileDirectoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileDirectoryResource.java new file mode 100644 index 000000000..3371fdb16 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileDirectoryResource.java @@ -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 { + + /** + * 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 directory, + DirectoryObjectTranslator translator, + DirectoryObjectResourceFactory resourceFactory) { + super(userContext, directory, translator, resourceFactory); + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileModule.java new file mode 100644 index 000000000..dabec70f5 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileModule.java @@ -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>() {}, + SharingProfileDirectoryResource.class + ) + .build(new TypeLiteral>() {})); + + // Create the required DirectoryObjectResourceFactory implementation + install(new FactoryModuleBuilder() + .implement( + new TypeLiteral>() {}, + SharingProfileResource.class + ) + .build(new TypeLiteral>() {})); + + // Bind translator for converting between SharingProfile and APISharingProfile + bind(new TypeLiteral>() {}) + .to(SharingProfileObjectTranslator.class); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileObjectTranslator.java b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileObjectTranslator.java new file mode 100644 index 000000000..42dbc1cad --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileObjectTranslator.java @@ -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 { + + @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()); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileResource.java new file mode 100644 index 000000000..debf804b2 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileResource.java @@ -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 { + + /** + * 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 directory, + @Assisted SharingProfile sharingProfile, + DirectoryObjectTranslator 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 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(); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/package-info.java new file mode 100644 index 000000000..53a76f710 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/package-info.java @@ -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; + diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/user/APIUserWrapper.java b/guacamole/src/main/java/org/apache/guacamole/rest/user/APIUserWrapper.java index f9a05bb38..cef6aec88 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/user/APIUserWrapper.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/user/APIUserWrapper.java @@ -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 { From e4fe1a3a6512b4f9b40a479b4cb945c3a07c159d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 15 Jul 2016 21:17:57 -0700 Subject: [PATCH 11/18] GUACAMOLE-5: Associate the UserContext with any created tunnel. --- .../apache/guacamole/GuacamoleSession.java | 10 +- .../rest/tunnel/TunnelCollectionResource.java | 6 +- .../guacamole/rest/tunnel/TunnelResource.java | 6 +- .../tunnel/TunnelRequestService.java | 14 +- .../apache/guacamole/tunnel/UserTunnel.java | 120 ++++++++++++++++++ 5 files changed, 140 insertions(+), 16 deletions(-) create mode 100644 guacamole/src/main/java/org/apache/guacamole/tunnel/UserTunnel.java diff --git a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java index 41832cb34..0170e4d9a 100644 --- a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java +++ b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java @@ -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 tunnels = - new ConcurrentHashMap(); + private final Map tunnels = + new ConcurrentHashMap(); /** * 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 getTunnels() { + public Map 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); } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java index cd9cb4eb7..cc00e139a 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java @@ -30,7 +30,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. @@ -89,10 +89,10 @@ public class TunnelCollectionResource { public TunnelResource getTunnel(@PathParam("tunnel") String tunnelUUID) throws GuacamoleException { - Map tunnels = session.getTunnels(); + Map 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."); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java index 3bfcae200..0bda8f36b 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java @@ -27,7 +27,7 @@ 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.tunnel.UserTunnel; /** * A REST resource which abstracts the operations available for an individual @@ -48,7 +48,7 @@ public class TunnelResource { /** * The tunnel that this TunnelResource represents. */ - private final StreamInterceptingTunnel tunnel; + private final UserTunnel tunnel; /** * Creates a new TunnelResource which exposes the operations and @@ -57,7 +57,7 @@ public class TunnelResource { * @param tunnel * The tunnel that this TunnelResource should represent. */ - public TunnelResource(StreamInterceptingTunnel tunnel) { + public TunnelResource(UserTunnel tunnel) { this.tunnel = tunnel; } diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java index 95e4bb9ac..5bcab4cef 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java @@ -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); } diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/UserTunnel.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/UserTunnel.java new file mode 100644 index 000000000..ab1b927e5 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/UserTunnel.java @@ -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 activeConnectionDirectory = userContext.getActiveConnectionDirectory(); + Collection 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; + + } + +} From aeb60e7e53695df65b659db02bf8d460e5a1840e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Jul 2016 16:14:24 -0700 Subject: [PATCH 12/18] GUACAMOLE-5: Add ActiveConnection subresource to the tunnel REST resource. --- .../guacamole/rest/RESTServiceModule.java | 4 ++ .../rest/session/SessionResource.java | 10 ++++- .../rest/tunnel/TunnelCollectionResource.java | 14 +++++- .../TunnelCollectionResourceFactory.java | 45 +++++++++++++++++++ .../guacamole/rest/tunnel/TunnelResource.java | 44 +++++++++++++++++- .../rest/tunnel/TunnelResourceFactory.java | 44 ++++++++++++++++++ 6 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResourceFactory.java create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResourceFactory.java diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java index 7b7322b8e..efe9766ad 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java @@ -40,6 +40,8 @@ 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; /** @@ -91,6 +93,8 @@ 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 diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResource.java index 7c7f14f80..93f00a5e6 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResource.java @@ -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); } } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java index cc00e139a..600254f28 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java @@ -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; @@ -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; } @@ -97,7 +107,7 @@ public class TunnelCollectionResource { throw new GuacamoleResourceNotFoundException("No such tunnel."); // Return corresponding tunnel resource - return new TunnelResource(tunnel); + return tunnelResourceFactory.create(tunnel); } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResourceFactory.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResourceFactory.java new file mode 100644 index 000000000..350e1a650 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResourceFactory.java @@ -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); + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java index 0bda8f36b..6583a0774 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java @@ -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,6 +30,11 @@ 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.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; /** @@ -50,6 +58,14 @@ public class TunnelResource { */ private final UserTunnel tunnel; + /** + * A factory which can be used to create instances of resources representing + * ActiveConnections. + */ + @Inject + private DirectoryObjectResourceFactory + activeConnectionResourceFactory; + /** * Creates a new TunnelResource which exposes the operations and * subresources available for the given tunnel. @@ -57,10 +73,36 @@ public class TunnelResource { * @param tunnel * The tunnel that this TunnelResource should represent. */ - public TunnelResource(UserTunnel 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 + 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. * diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResourceFactory.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResourceFactory.java new file mode 100644 index 000000000..cafd64e2c --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResourceFactory.java @@ -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); + +} From 3dce3bc5b862b883dfa479dedaa89d49b3c86253 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Jul 2016 16:03:08 -0700 Subject: [PATCH 13/18] GUACAMOLE-5: Add Connection subresource to the ActiveConnection resource. --- .../ActiveConnectionResource.java | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java index d006416ba..6bbfaf7b7 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java @@ -19,16 +19,22 @@ 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.Path; 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 +47,25 @@ import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; public class ActiveConnectionResource extends DirectoryObjectResource { + /** + * 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 + connectionDirectoryResourceFactory; + /** * Creates a new ActiveConnectionResource which exposes the operations and * subresources available for the given ActiveConnection. @@ -51,7 +76,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 +87,33 @@ public class ActiveConnectionResource @AssistedInject public ActiveConnectionResource(@Assisted UserContext userContext, @Assisted Directory directory, - @Assisted ActiveConnection connection, + @Assisted ActiveConnection activeConnection, DirectoryObjectTranslator 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 getConnection() + throws GuacamoleException { + + // Return the underlying connection as a resource + return connectionDirectoryResourceFactory + .create(userContext, userContext.getConnectionDirectory()) + .getObjectResource(activeConnection.getConnectionIdentifier()); + } } From c6ce92bd0a35a55dd678ceb83b888385b72ac755 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 15 Jul 2016 21:58:59 -0700 Subject: [PATCH 14/18] GUACAMOLE-5: Implement retrieval of sharing credentials from an active connection via REST. --- .../activeconnection/APIUserCredentials.java | 82 +++++++++++++++++++ .../ActiveConnectionResource.java | 27 ++++++ 2 files changed, 109 insertions(+) create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/APIUserCredentials.java diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/APIUserCredentials.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/APIUserCredentials.java new file mode 100644 index 000000000..eb87bafcb --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/APIUserCredentials.java @@ -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 expected; + + /** + * A map of all field values by field name. + */ + private final Map 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 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 getValues() { + return values; + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java index 6bbfaf7b7..736cbf8b9 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java @@ -23,7 +23,9 @@ 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; @@ -116,4 +118,29 @@ public class ActiveConnectionResource } + /** + * 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)); + + } + } From 62dcd9e9ec29e2656ac43a9c33522ad032332e93 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 15 Jul 2016 18:41:17 -0700 Subject: [PATCH 15/18] GUACAMOLE-5: Implement JavaScript service for retrieving sharing credentials from an active connection. --- .../rest/services/activeConnectionService.js | 35 +++++ .../webapp/app/rest/services/tunnelService.js | 37 +++++ .../webapp/app/rest/types/UserCredentials.js | 133 ++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 guacamole/src/main/webapp/app/rest/types/UserCredentials.js diff --git a/guacamole/src/main/webapp/app/rest/services/activeConnectionService.js b/guacamole/src/main/webapp/app/rest/services/activeConnectionService.js index 9c967be32..7c8fb6a35 100644 --- a/guacamole/src/main/webapp/app/rest/services/activeConnectionService.js +++ b/guacamole/src/main/webapp/app/rest/services/activeConnectionService.js @@ -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.} + * 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; }]); diff --git a/guacamole/src/main/webapp/app/rest/services/tunnelService.js b/guacamole/src/main/webapp/app/rest/services/tunnelService.js index e93c50fb9..1395b2581 100644 --- a/guacamole/src/main/webapp/app/rest/services/tunnelService.js +++ b/guacamole/src/main/webapp/app/rest/services/tunnelService.js @@ -68,6 +68,43 @@ angular.module('rest').factory('tunnelService', ['$injector', }; + /** + * 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.} + * 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 diff --git a/guacamole/src/main/webapp/app/rest/types/UserCredentials.js b/guacamole/src/main/webapp/app/rest/types/UserCredentials.js new file mode 100644 index 000000000..ddf9eaf3e --- /dev/null +++ b/guacamole/src/main/webapp/app/rest/types/UserCredentials.js @@ -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. + */ + 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; + +}]); From bb36045ff8df506d890311aefacdd6a395434a98 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 16 Jul 2016 00:31:23 -0700 Subject: [PATCH 16/18] GUACAMOLE-5: Add DirectoryView object, providing a Directory interface around a restricted subset of objects within another existing Directory. --- .../rest/directory/DirectoryView.java | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryView.java diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryView.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryView.java new file mode 100644 index 000000000..aee18f346 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryView.java @@ -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 + * The type of objects accessible through this DirectoryView. + */ +public class DirectoryView + implements Directory { + + /** + * The Directory from which the given set of objects will be retrieved. + */ + private final Directory directory; + + /** + * The set of identifiers representing the restricted set of objects that + * this DirectoryView should provide access to. + */ + private final Set 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 directory, + Set 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 getAll(Collection identifiers) + throws GuacamoleException { + + // Reduce requested identifiers to only those which occur within the + // originally-specified subset + identifiers = new ArrayList(identifiers); + identifiers.retainAll(this.identifiers); + + // Delegate to underlying directory + return directory.getAll(identifiers); + + } + + @Override + public Set 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"); + } + +} From 7ea4af7016152ceb9154e9c2aa54bf5d25caab17 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 16 Jul 2016 00:37:52 -0700 Subject: [PATCH 17/18] GUACAMOLE-5: Provide direct access via REST to the sharing profiles available for the active connection of a given tunnel. --- .../rest/connection/ConnectionResource.java | 43 ++++++++++ .../webapp/app/rest/services/tunnelService.js | 30 +++++++ .../webapp/app/rest/types/SharingProfile.js | 85 +++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 guacamole/src/main/webapp/app/rest/types/SharingProfile.js diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java index 9238af15e..4d257701d 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java @@ -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 + 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 + getSharingProfileDirectoryResource() throws GuacamoleException { + + // Produce subset of all SharingProfiles, containing only those which + // are associated with this connection + Directory sharingProfiles = new DirectoryView( + userContext.getSharingProfileDirectory(), + connection.getSharingProfileIdentifiers() + ); + + // Return a new resource which provides access to only those SharingProfiles + return sharingProfileDirectoryResourceFactory.create(userContext, sharingProfiles); + + } + } diff --git a/guacamole/src/main/webapp/app/rest/services/tunnelService.js b/guacamole/src/main/webapp/app/rest/services/tunnelService.js index 1395b2581..b9dc0cd47 100644 --- a/guacamole/src/main/webapp/app/rest/services/tunnelService.js +++ b/guacamole/src/main/webapp/app/rest/services/tunnelService.js @@ -68,6 +68,36 @@ 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.>} + * 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 diff --git a/guacamole/src/main/webapp/app/rest/types/SharingProfile.js b/guacamole/src/main/webapp/app/rest/types/SharingProfile.js new file mode 100644 index 000000000..ea8287d0a --- /dev/null +++ b/guacamole/src/main/webapp/app/rest/types/SharingProfile.js @@ -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. + */ + 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. + */ + this.attributes = {}; + + }; + + return SharingProfile; + +}]); \ No newline at end of file From 9b2909445a7da2b4406a227edbedd79c6998be2d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 17 Jul 2016 14:54:32 -0700 Subject: [PATCH 18/18] GUACAMOLE-5: Move single space from UserTunnel to PermissionSetResource. --- .../apache/guacamole/rest/permission/PermissionSetResource.java | 2 +- .../src/main/java/org/apache/guacamole/tunnel/UserTunnel.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java index 3ad005c42..9dd38f758 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java @@ -212,7 +212,7 @@ public class PermissionSetResource { } - // Create sharing profile permission if path has sharing profileprefix + // 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 diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/UserTunnel.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/UserTunnel.java index ab1b927e5..2632a4c53 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/UserTunnel.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/UserTunnel.java @@ -98,7 +98,7 @@ public class UserTunnel extends StreamInterceptingTunnel { Directory activeConnectionDirectory = userContext.getActiveConnectionDirectory(); Collection activeConnectionIdentifiers = activeConnectionDirectory.getIdentifiers(); - // Search all connections for a tunnel which matches this tunnel + // Search all connections for a tunnel which matches this tunnel for (ActiveConnection activeConnection : activeConnectionDirectory.getAll(activeConnectionIdentifiers)) { // If we lack access, continue with next tunnel