diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedAuthenticatedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedAuthenticatedUser.java index 84166c658..958213cbc 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedAuthenticatedUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedAuthenticatedUser.java @@ -19,6 +19,8 @@ package org.apache.guacamole.auth.jdbc.sharing.user; +import java.util.Collections; +import java.util.Set; import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; @@ -100,4 +102,9 @@ public class SharedAuthenticatedUser extends RemoteAuthenticatedUser { throw new UnsupportedOperationException("Users authenticated via share keys are immutable."); } + @Override + public Set getEffectiveUserGroups() { + return Collections.emptySet(); + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java index 8e7931d86..697b2ca7f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java @@ -30,10 +30,13 @@ import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.Permissions; +import org.apache.guacamole.net.auth.RelatedObjectSet; import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.net.auth.permission.SystemPermissionSet; import org.apache.guacamole.net.auth.simple.SimpleObjectPermissionSet; +import org.apache.guacamole.net.auth.simple.SimpleRelatedObjectSet; import org.apache.guacamole.net.auth.simple.SimpleSystemPermissionSet; /** @@ -140,6 +143,11 @@ public class SharedUser implements User { return new SharedObjectPermissionSet(userDirectory.getIdentifiers()); } + @Override + public ObjectPermissionSet getUserGroupPermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(); + } + @Override public ObjectPermissionSet getSharingProfilePermissions() throws GuacamoleException { return new SimpleObjectPermissionSet(); @@ -150,4 +158,14 @@ public class SharedUser implements User { return new SimpleObjectPermissionSet(); } + @Override + public RelatedObjectSet getUserGroups() throws GuacamoleException { + return new SimpleRelatedObjectSet(); + } + + @Override + public Permissions getEffectivePermissions() throws GuacamoleException { + return this; + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java index 6d580baf6..8c201d004 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java @@ -169,4 +169,9 @@ public class ModeledAuthenticatedUser extends RemoteAuthenticatedUser { user.setIdentifier(identifier); } + @Override + public Set getEffectiveUserGroups() { + return Collections.emptySet(); + } + } 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 b29565551..583aa7fc1 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 @@ -52,10 +52,14 @@ import org.apache.guacamole.form.TextField; import org.apache.guacamole.form.TimeField; import org.apache.guacamole.form.TimeZoneField; import org.apache.guacamole.net.auth.ActivityRecord; +import org.apache.guacamole.net.auth.Permissions; +import org.apache.guacamole.net.auth.RelatedObjectSet; 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.apache.guacamole.net.auth.simple.SimpleRelatedObjectSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -379,6 +383,11 @@ public class ModeledUser extends ModeledDirectoryObject implements Us return userPermissionService.getPermissionSet(getCurrentUser(), this); } + @Override + public ObjectPermissionSet getUserGroupPermissions() throws GuacamoleException { + return new SimpleObjectPermissionSet(); + } + /** * Stores all restricted (privileged) attributes within the given Map, * pulling the values of those attributes from the underlying user model. @@ -839,4 +848,14 @@ public class ModeledUser extends ModeledDirectoryObject implements Us return userService.retrieveHistory(getCurrentUser(), this); } + @Override + public RelatedObjectSet getUserGroups() throws GuacamoleException { + return new SimpleRelatedObjectSet(); + } + + @Override + public Permissions getEffectivePermissions() throws GuacamoleException { + return this; + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java index 5bfcda675..cbc3448a7 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.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 java.util.Date; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.jdbc.base.RestrictedObject; @@ -46,6 +47,8 @@ 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.UserGroup; +import org.apache.guacamole.net.auth.simple.SimpleDirectory; /** * UserContext implementation which is driven by an arbitrary, underlying @@ -161,6 +164,11 @@ public class ModeledUserContext extends RestrictedObject return userDirectory; } + @Override + public Directory getUserGroupDirectory() throws GuacamoleException { + return new SimpleDirectory(); + } + @Override public Directory getConnectionDirectory() throws GuacamoleException { return connectionDirectory; @@ -214,6 +222,11 @@ public class ModeledUserContext extends RestrictedObject return ModeledUser.ATTRIBUTES; } + @Override + public Collection
getUserGroupAttributes() { + return Collections.emptyList(); + } + @Override public Collection getConnectionAttributes() { return ModeledConnection.ATTRIBUTES; diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractAuthenticatedUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractAuthenticatedUser.java index 08b9b4e56..36c4571e0 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractAuthenticatedUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractAuthenticatedUser.java @@ -19,6 +19,8 @@ package org.apache.guacamole.net.auth; +import java.util.Collections; +import java.util.Set; /** * Basic implementation of an AuthenticatedUser which uses the username to @@ -29,6 +31,11 @@ public abstract class AbstractAuthenticatedUser extends AbstractIdentifiable // Prior functionality now resides within AbstractIdentifiable + @Override + public Set getEffectiveUserGroups() { + return Collections.emptySet(); + } + @Override public void invalidate() { // Nothing to invalidate diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserContext.java index e2de612e3..eb31f7edb 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserContext.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUserContext.java @@ -67,6 +67,19 @@ public abstract class AbstractUserContext implements UserContext { return new SimpleDirectory(self()); } + /** + * {@inheritDoc} + * + *

This implementation simply returns an empty {@link Directory}. + * Implementations that wish to expose user groups should override this + * function. + */ + @Override + public Directory getUserGroupDirectory() + throws GuacamoleException { + return new SimpleDirectory(); + } + /** * {@inheritDoc} * @@ -181,6 +194,18 @@ public abstract class AbstractUserContext implements UserContext { return Collections.emptyList(); } + /** + * {@inheritDoc} + * + *

This implementation simply returns an empty {@link Collection}. + * Implementations that wish to expose custom user group attributes as + * fields within user group edit screens should override this function. + */ + @Override + public Collection getUserGroupAttributes() { + return Collections.emptyList(); + } + /** * {@inheritDoc} * diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticatedUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticatedUser.java index f6ceb3abe..a799937e8 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticatedUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AuthenticatedUser.java @@ -19,6 +19,7 @@ package org.apache.guacamole.net.auth; +import java.util.Set; /** * A user of the Guacamole web application who has been authenticated by an @@ -49,6 +50,24 @@ public interface AuthenticatedUser extends Identifiable { */ Credentials getCredentials(); + /** + * Returns a read-only set of the identifiers of all user groups which + * apply to this authenticated user. The exact semantics of what user + * groups apply are up to the implementation, and the user groups within + * this set may be implied, derived dynamically, inherited through multiple + * levels of group membership, etc. + * + * Note that, as with user identifiers, user group identifiers form the + * basis of identity which applies across authentication providers. It is + * expected that any two user groups having the same identifier represent + * the same group, even if defined by different authentication providers. + * + * @return + * A read-only set of the identifiers of all user groups which apply + * to this authenticated user. + */ + Set getEffectiveUserGroups(); + /** * Invalidates this authenticated user and their associated token such that * they are no longer logged in. This function will be automatically diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUser.java index 65f057778..92873c48e 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUser.java @@ -124,4 +124,20 @@ public class DelegatingUser implements User { return user.getUserPermissions(); } + @Override + public ObjectPermissionSet getUserGroupPermissions() + throws GuacamoleException { + return user.getUserGroupPermissions(); + } + + @Override + public RelatedObjectSet getUserGroups() throws GuacamoleException { + return user.getUserGroups(); + } + + @Override + public Permissions getEffectivePermissions() throws GuacamoleException { + return user.getEffectivePermissions(); + } + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java index a37faf952..00788f823 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java @@ -65,6 +65,11 @@ public class DelegatingUserContext implements UserContext { return userContext.getUserDirectory(); } + @Override + public Directory getUserGroupDirectory() throws GuacamoleException { + return userContext.getUserGroupDirectory(); + } + @Override public Directory getConnectionDirectory() throws GuacamoleException { @@ -111,6 +116,11 @@ public class DelegatingUserContext implements UserContext { return userContext.getUserAttributes(); } + @Override + public Collection getUserGroupAttributes() { + return userContext.getUserGroupAttributes(); + } + @Override public Collection getConnectionAttributes() { return userContext.getConnectionAttributes(); diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserGroup.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserGroup.java new file mode 100644 index 000000000..5093a21be --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserGroup.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.net.auth; + +import java.util.Map; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; +import org.apache.guacamole.net.auth.permission.SystemPermissionSet; + +/** + * UserGroup implementation which simply delegates all function calls to an + * underlying UserGroup. + */ +public class DelegatingUserGroup implements UserGroup { + + /** + * The wrapped UserGroup. + */ + private final UserGroup userGroup; + + /** + * Wraps the given UserGroup such that all function calls against this + * DelegatingUserGroup will be delegated to it. + * + * @param userGroup + * The UserGroup to wrap. + */ + public DelegatingUserGroup(UserGroup userGroup) { + this.userGroup = userGroup; + } + + @Override + public String getIdentifier() { + return userGroup.getIdentifier(); + } + + @Override + public void setIdentifier(String identifier) { + userGroup.setIdentifier(identifier); + } + + @Override + public Map getAttributes() { + return userGroup.getAttributes(); + } + + @Override + public void setAttributes(Map attributes) { + userGroup.setAttributes(attributes); + } + + @Override + public SystemPermissionSet getSystemPermissions() + throws GuacamoleException { + return userGroup.getSystemPermissions(); + } + + @Override + public ObjectPermissionSet getConnectionPermissions() + throws GuacamoleException { + return userGroup.getConnectionPermissions(); + } + + @Override + public ObjectPermissionSet getConnectionGroupPermissions() + throws GuacamoleException { + return userGroup.getConnectionGroupPermissions(); + } + + @Override + public ObjectPermissionSet getSharingProfilePermissions() + throws GuacamoleException { + return userGroup.getSharingProfilePermissions(); + } + + @Override + public ObjectPermissionSet getActiveConnectionPermissions() + throws GuacamoleException { + return userGroup.getActiveConnectionPermissions(); + } + + @Override + public ObjectPermissionSet getUserPermissions() throws GuacamoleException { + return userGroup.getUserPermissions(); + } + + @Override + public ObjectPermissionSet getUserGroupPermissions() + throws GuacamoleException { + return userGroup.getUserGroupPermissions(); + } + + @Override + public RelatedObjectSet getUserGroups() throws GuacamoleException { + return userGroup.getUserGroups(); + } + + @Override + public RelatedObjectSet getMemberUsers() throws GuacamoleException { + return userGroup.getMemberUsers(); + } + + @Override + public RelatedObjectSet getMemberUserGroups() throws GuacamoleException { + return userGroup.getMemberUserGroups(); + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Permissions.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Permissions.java new file mode 100644 index 000000000..3b698bef7 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Permissions.java @@ -0,0 +1,129 @@ +/* + * 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.permission.ObjectPermissionSet; +import org.apache.guacamole.net.auth.permission.SystemPermissionSet; + +/** + * An object which may be granted permissions to access/manipulate various + * other objects or aspects of the system. The permissions granted are exposed + * through subclasses of PermissionSet, and may be mutable depending on the + * access level of the current user. + */ +public interface Permissions { + + /** + * Returns all permissions given to this object regarding currently-active + * connections. + * + * @return + * An ObjectPermissionSet of all active connection permissions granted + * to this object. + * + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. + */ + ObjectPermissionSet getActiveConnectionPermissions() + throws GuacamoleException; + + /** + * Returns all connection group permissions given to this object. + * + * @return + * An ObjectPermissionSet of all connection group permissions granted + * to this object. + * + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. + */ + ObjectPermissionSet getConnectionGroupPermissions() + throws GuacamoleException; + + /** + * Returns all connection permissions given to this object. + * + * @return + * An ObjectPermissionSet of all connection permissions granted to this + * object. + * + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. + */ + ObjectPermissionSet getConnectionPermissions() throws GuacamoleException; + + /** + * Returns all sharing profile permissions given to this object. + * + * @return + * An ObjectPermissionSet of all sharing profile permissions granted to + * this object. + * + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. + */ + ObjectPermissionSet getSharingProfilePermissions() + throws GuacamoleException; + + /** + * Returns all system-level permissions given to this object. + * + * @return + * A SystemPermissionSet of all system-level permissions granted to + * this object. + * + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. + */ + SystemPermissionSet getSystemPermissions() throws GuacamoleException; + + /** + * Returns all user permissions given to this object. + * + * @return + * An ObjectPermissionSet of all user permissions granted to this + * object. + * + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. + */ + ObjectPermissionSet getUserPermissions() throws GuacamoleException; + + /** + * Returns all user group permissions given to this object. + * + * @return + * An ObjectPermissionSet of all user group permissions granted to this + * object. + * + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. + */ + ObjectPermissionSet getUserGroupPermissions() throws GuacamoleException; + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/RelatedObjectSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/RelatedObjectSet.java new file mode 100644 index 000000000..58bee372e --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/RelatedObjectSet.java @@ -0,0 +1,78 @@ +/* + * 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.Set; +import org.apache.guacamole.GuacamoleException; + +/** + * An arbitrary set of existing objects sharing some common relation. Unlike a + * Directory, which provides for maintaining the entire lifecycle of its + * objects, a RelatedObjectSet only maintains the relation between its + * containing object and the objects within the set. Adding/removing an object + * from a RelatedObjectSet affects only the status of the specific relationship + * represented by the RelatedObjectSet, not the existence of the objects + * themselves. + */ +public interface RelatedObjectSet { + + /** + * Returns a Set which contains the identifiers of all objects contained + * within this RelatedObjectSet. + * + * @return + * A Set which contains the identifiers of all objects contained + * within this RelatedObjectSet. + * + * @throws GuacamoleException + * If an error occurs while retrieving the objects within the set, or + * if objects cannot be retrieved due to lack of permissions to do so. + */ + Set getObjects() throws GuacamoleException; + + /** + * Adds the objects having the given identifiers, if not already present. + * If a specified object is already present, no operation is performed + * regarding that object. + * + * @param identifiers + * The identifiers of all objects being added. + * + * @throws GuacamoleException + * If an error occurs while adding the objects, or if permission to add + * objects is denied. + */ + void addObjects(Set identifiers) throws GuacamoleException; + + /** + * Removes each of the objects having the specified identifiers, if + * present. If a specified object is not present, no operation is performed + * regarding that object. + * + * @param identifiers + * The identifiers of all objects being removed. + * + * @throws GuacamoleException + * If an error occurs while removing the objects, or if permission to + * remove objects is denied. + */ + void removeObjects(Set identifiers) throws GuacamoleException; + +} 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 a39a772f0..4a8b43a8d 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 @@ -22,14 +22,11 @@ package org.apache.guacamole.net.auth; import java.util.Date; import java.util.List; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; -import org.apache.guacamole.net.auth.permission.SystemPermissionSet; - /** * A user of the Guacamole web application. */ -public interface User extends Identifiable, Attributes { +public interface User extends Identifiable, Attributes, Permissions { /** * All standard attribute names with semantics defined by the Guacamole web @@ -109,85 +106,33 @@ public interface User extends Identifiable, Attributes { List getHistory() throws GuacamoleException; /** - * Returns all system-level permissions given to this user. + * Returns a set of all readable user groups of which this user is a member. + * If permission is granted for the current user to modify the membership of + * this user, then the returned set will be mutable, and any such + * modifications should be made through changes to the returned set. * * @return - * A SystemPermissionSet of all system-level permissions granted to - * this user. + * The set of all readable user groups of which this user is a member. * - * @throws GuacamoleException - * If an error occurs while retrieving permissions, or if reading all - * permissions is not allowed. + * @throws GuacamoleException + * If an error occurs while retrieving the user groups. */ - SystemPermissionSet getSystemPermissions() throws GuacamoleException; + RelatedObjectSet getUserGroups() throws GuacamoleException; /** - * Returns all connection permissions given to this user. + * Returns a read-only view of all permissions granted to this user. The + * exact semantics of what permissions are granted are up to the + * implementation, and the permissions within this view may be implied, + * derived dynamically, inherited through multiple levels of group + * membership, etc. * * @return - * An ObjectPermissionSet of all connection permissions granted to this - * user. - * - * @throws GuacamoleException - * If an error occurs while retrieving permissions, or if reading all - * permissions is not allowed. - */ - ObjectPermissionSet getConnectionPermissions() - throws GuacamoleException; - - /** - * Returns all connection group permissions given to this user. - * - * @return - * An ObjectPermissionSet of all connection group permissions granted - * to this user. + * A read-only view of the permissions which are granted to this user. * * @throws GuacamoleException * If an error occurs while retrieving permissions, or if reading all * permissions is not allowed. */ - 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. - * - * @return - * An ObjectPermissionSet of all active connection permissions granted - * to this user. - * - * @throws GuacamoleException - * If an error occurs while retrieving permissions, or if reading all - * permissions is not allowed. - */ - ObjectPermissionSet getActiveConnectionPermissions() - throws GuacamoleException; - - /** - * Returns all user permissions given to this user. - * - * @return - * An ObjectPermissionSet of all user permissions granted to this user. - * - * @throws GuacamoleException - * If an error occurs while retrieving permissions, or if reading all - * permissions is not allowed. - */ - ObjectPermissionSet getUserPermissions() throws GuacamoleException; + Permissions getEffectivePermissions() throws GuacamoleException; } 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 1c82f9ce8..ea7c8c40e 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 @@ -84,6 +84,20 @@ public interface UserContext { */ Directory getUserDirectory() throws GuacamoleException; + /** + * Retrieves a Directory which can be used to view and manipulate user + * groups, but only as allowed by the permissions given to the user of this + * UserContext. + * + * @return + * A Directory whose operations are bound by the restrictions + * of this UserContext. + * + * @throws GuacamoleException + * If an error occurs while creating the Directory. + */ + Directory getUserGroupDirectory() throws GuacamoleException; + /** * Retrieves a Directory which can be used to view and manipulate * connections and their configurations, but only as allowed by the @@ -197,6 +211,17 @@ public interface UserContext { */ Collection getUserAttributes(); + /** + * Retrieves a collection of all attributes applicable to user groups. 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 user groups. + */ + Collection getUserGroupAttributes(); + /** * Retrieves a collection of all attributes applicable to connections. This * collection will contain only those attributes which the current user has diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserGroup.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserGroup.java new file mode 100644 index 000000000..4dd167d13 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserGroup.java @@ -0,0 +1,77 @@ +/* + * 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; + +/** + * A user group of the Guacamole web application. Each user group may contain + * any number of Guacamole users and other user groups, and defines the + * permissions implicitly granted to its members. + */ +public interface UserGroup extends Identifiable, Attributes, Permissions { + + /** + * Returns a set of all readable user groups of which this user group is a + * member. If permission is granted for the current user to modify the + * membership of this user group, then the returned set will be mutable, + * and any such modifications should be made through changes to the + * returned set. + * + * @return + * The set of all readable user groups of which this user group is a + * member. + * + * @throws GuacamoleException + * If an error occurs while retrieving the user groups. + */ + RelatedObjectSet getUserGroups() throws GuacamoleException; + + /** + * Returns a set of all readable users that are members of this user group. + * If permission is granted for the current user to modify the members of + * this group, then the returned set will be mutable, and any such + * modifications should be made through changes to the returned set. + * + * @return + * The set all readable users that are members of this user group, + * which may be mutable. + * + * @throws GuacamoleException + * If an error occurs while retrieving the users. + */ + RelatedObjectSet getMemberUsers() throws GuacamoleException; + + /** + * Returns a set of all readable user groups that are members of this user + * group. If permission is granted for the current user to modify the + * members of this group, then the returned set will be mutable, and any + * such modifications should be made through changes to the returned set. + * + * @return + * The set of all readable user groups that are members of this user + * group, which may be mutable. + * + * @throws GuacamoleException + * If an error occurs while retrieving the user groups. + */ + RelatedObjectSet getMemberUserGroups() throws GuacamoleException; + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java index 837f1d701..7b1e3e7f0 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleAuthenticationProvider.java @@ -19,7 +19,9 @@ package org.apache.guacamole.net.auth.simple; +import java.util.Collections; import java.util.Map; +import java.util.Set; import java.util.UUID; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; @@ -131,6 +133,11 @@ public abstract class SimpleAuthenticationProvider return credentials; } + @Override + public Set getEffectiveUserGroups() { + return Collections.emptySet(); + } + } /** diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleRelatedObjectSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleRelatedObjectSet.java new file mode 100644 index 000000000..72f2da9f1 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleRelatedObjectSet.java @@ -0,0 +1,88 @@ +/* + * 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.simple; + +import java.util.Collections; +import java.util.Set; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.net.auth.RelatedObjectSet; + +/** + * A read-only implementation of RelatedObjectSet which uses a backing Set + * of identifiers to determine which objects are present. + */ +public class SimpleRelatedObjectSet implements RelatedObjectSet { + + /** + * A set containing the identifiers of all objects currently present. + */ + private Set identifiers = Collections.emptySet(); + + /** + * Creates a new empty SimpleObjectPermissionSet. + */ + public SimpleRelatedObjectSet() { + } + + /** + * Creates a new SimpleRelatedObjectSet which contains the objects having + * the identifiers within the given Set. The given Set backs the contents + * of the new SimpleRelatedObjectSet. While the SimpleRelatedObjectSet is + * read-only, any changes to the underlying Set will be reflected in the + * SimpleRelatedObjectSet. + * + * @param identifiers + * The Set containing the identifiers of all objects which should be + * present within the new SimpleRelatedObjectSet. + */ + public SimpleRelatedObjectSet(Set identifiers) { + this.identifiers = identifiers; + } + + /** + * Replaces the Set of object identifiers which backs this + * SimpleRelatedObjectSet. Future function calls on this + * SimpleRelatedObjectSet will instead use the provided Set. + * + * @param identifiers + * The Set containing the identifiers of all objects which should be + * present within this SimpleRelatedObjectSet. + */ + protected void setObjects(Set identifiers) { + this.identifiers = identifiers; + } + + @Override + public Set getObjects() { + return identifiers; + } + + @Override + public void addObjects(Set identifiers) throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void removeObjects(Set identifiers) throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + +} 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 19ed35731..61fce20f4 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 @@ -29,6 +29,8 @@ import java.util.Set; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.AbstractUser; import org.apache.guacamole.net.auth.ActivityRecord; +import org.apache.guacamole.net.auth.Permissions; +import org.apache.guacamole.net.auth.RelatedObjectSet; import org.apache.guacamole.net.auth.permission.ObjectPermission; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.net.auth.permission.SystemPermissionSet; @@ -200,6 +202,12 @@ public class SimpleUser extends AbstractUser { return new SimpleObjectPermissionSet(userPermissions); } + @Override + public ObjectPermissionSet getUserGroupPermissions() + throws GuacamoleException { + return new SimpleObjectPermissionSet(); + } + @Override public ObjectPermissionSet getActiveConnectionPermissions() throws GuacamoleException { @@ -211,4 +219,14 @@ public class SimpleUser extends AbstractUser { return new SimpleObjectPermissionSet(); } + @Override + public RelatedObjectSet getUserGroups() throws GuacamoleException { + return new SimpleRelatedObjectSet(); + } + + @Override + public Permissions getEffectivePermissions() throws GuacamoleException { + return this; + } + } 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 0b63b66e5..3149987d1 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 @@ -35,9 +35,9 @@ 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.net.auth.Permissions; 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; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; @@ -119,11 +119,12 @@ public class ConnectionResource extends DirectoryObjectResource getConnectionParameters() throws GuacamoleException { - User self = userContext.self(); + // Pull effective permissions + Permissions effective = userContext.self().getEffectivePermissions(); // Retrieve permission sets - SystemPermissionSet systemPermissions = self.getSystemPermissions(); - ObjectPermissionSet connectionPermissions = self.getConnectionPermissions(); + SystemPermissionSet systemPermissions = effective.getSystemPermissions(); + ObjectPermissionSet connectionPermissions = effective.getConnectionPermissions(); // Deny access if adminstrative or update permission is missing String identifier = connection.getIdentifier(); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java index a85096667..1a3cb5fc0 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java @@ -29,8 +29,8 @@ import org.apache.guacamole.GuacamoleException; 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.Permissions; 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; @@ -356,9 +356,9 @@ public class ConnectionGroupTree { retrievedGroups.put(root.getIdentifier(), this.rootAPIGroup); // Store user's current permissions - User self = userContext.self(); - this.connectionPermissions = self.getConnectionPermissions(); - this.sharingProfilePermissions = self.getSharingProfilePermissions(); + Permissions effective = userContext.self().getEffectivePermissions(); + this.connectionPermissions = effective.getConnectionPermissions(); + this.sharingProfilePermissions = effective.getSharingProfilePermissions(); // Store required directories this.connectionDirectory = userContext.getConnectionDirectory(); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java index 3cbd4817d..9973301a2 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java @@ -37,7 +37,7 @@ import org.apache.guacamole.GuacamoleResourceNotFoundException; import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.net.auth.Directory; import org.apache.guacamole.net.auth.Identifiable; -import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.Permissions; import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.permission.ObjectPermission; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; @@ -142,14 +142,14 @@ public abstract class DirectoryResource identifiers = directory.getIdentifiers(); if (!isAdmin && permissions != null && !permissions.isEmpty()) { - ObjectPermissionSet objectPermissions = self.getUserPermissions(); + ObjectPermissionSet objectPermissions = effective.getUserPermissions(); identifiers = objectPermissions.getAccessibleObjects(permissions, identifiers); } 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 5c15a2b9f..18c38e86a 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 @@ -24,20 +24,20 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.Permissions; 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; /** - * The set of permissions which are granted to a specific user, organized by - * object type and, if applicable, identifier. This object can be constructed - * with arbitrary permissions present, or manipulated after creation through - * the manipulation or replacement of its collections of permissions, but is - * otherwise not intended for internal use as a data structure for permissions. - * Its primary purpose is as a hierarchical format for exchanging granted - * permissions with REST clients. + * The set of permissions which are granted to a specific user or user group, + * organized by object type and, if applicable, identifier. This object can be + * constructed with arbitrary permissions present, or manipulated after creation + * through the manipulation or replacement of its collections of permissions, + * but is otherwise not intended for internal use as a data structure for + * permissions. Its primary purpose is as a hierarchical format for exchanging + * granted permissions with REST clients. */ public class APIPermissionSet { @@ -146,24 +146,23 @@ public class APIPermissionSet { /** * Creates a new permission set containing all permissions currently - * granted to the given user. + * granted within the given Permissions object. * - * @param user - * The user whose permissions should be stored within this permission - * set. + * @param permissions + * The permissions that should be stored within this permission set. * * @throws GuacamoleException - * If an error occurs while retrieving the user's permissions. + * If an error occurs while retrieving the permissions. */ - public APIPermissionSet(User user) throws GuacamoleException { + public APIPermissionSet(Permissions permissions) throws GuacamoleException { // Add all permissions from the provided user - addSystemPermissions(systemPermissions, user.getSystemPermissions()); - addObjectPermissions(connectionPermissions, user.getConnectionPermissions()); - addObjectPermissions(connectionGroupPermissions, user.getConnectionGroupPermissions()); - addObjectPermissions(sharingProfilePermissions, user.getSharingProfilePermissions()); - addObjectPermissions(activeConnectionPermissions, user.getActiveConnectionPermissions()); - addObjectPermissions(userPermissions, user.getUserPermissions()); + addSystemPermissions(systemPermissions, permissions.getSystemPermissions()); + addObjectPermissions(connectionPermissions, permissions.getConnectionPermissions()); + addObjectPermissions(connectionGroupPermissions, permissions.getConnectionGroupPermissions()); + addObjectPermissions(sharingProfilePermissions, permissions.getSharingProfilePermissions()); + addObjectPermissions(activeConnectionPermissions, permissions.getActiveConnectionPermissions()); + addObjectPermissions(userPermissions, permissions.getUserPermissions()); } @@ -229,7 +228,7 @@ public class APIPermissionSet { /** * Returns a map of user IDs to the set of permissions granted for that - * user. If no permissions are granted to a particular user, its ID will + * user. If no permissions are granted for a particular user, its ID will * not be present as a key in the map. This map is mutable, and changes to * to this map will affect the permission set directly. * 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 index 7b96de904..4797ade7f 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileResource.java @@ -30,8 +30,8 @@ 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.Permissions; 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; @@ -103,11 +103,12 @@ public class SharingProfileResource public Map getParameters() throws GuacamoleException { - User self = userContext.self(); + // Pull effective permissions + Permissions effective = userContext.self().getEffectivePermissions(); // Retrieve permission sets - SystemPermissionSet systemPermissions = self.getSystemPermissions(); - ObjectPermissionSet sharingProfilePermissions = self.getSharingProfilePermissions(); + SystemPermissionSet systemPermissions = effective.getSystemPermissions(); + ObjectPermissionSet sharingProfilePermissions = effective.getSharingProfilePermissions(); // Deny access if adminstrative or update permission is missing String identifier = sharingProfile.getIdentifier(); 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 c4b85f9ae..c4375668d 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 @@ -26,6 +26,8 @@ import java.util.Map; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.net.auth.ActivityRecord; +import org.apache.guacamole.net.auth.Permissions; +import org.apache.guacamole.net.auth.RelatedObjectSet; import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.net.auth.permission.SystemPermissionSet; @@ -110,12 +112,28 @@ public class APIUserWrapper implements User { throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); } + @Override + public ObjectPermissionSet getUserGroupPermissions() + throws GuacamoleException { + throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); + } + @Override public ObjectPermissionSet getActiveConnectionPermissions() throws GuacamoleException { throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); } + @Override + public Permissions getEffectivePermissions() throws GuacamoleException { + throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); + } + + @Override + public RelatedObjectSet getUserGroups() throws GuacamoleException { + throw new GuacamoleUnsupportedException("APIUserWrapper does not provide group access."); + } + @Override public Date getLastActive() { return null; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java index 06bab9f43..72c916219 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java @@ -43,6 +43,7 @@ import org.apache.guacamole.net.auth.credentials.GuacamoleCredentialsException; import org.apache.guacamole.rest.directory.DirectoryObjectResource; import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; import org.apache.guacamole.rest.history.APIActivityRecord; +import org.apache.guacamole.rest.permission.APIPermissionSet; import org.apache.guacamole.rest.permission.PermissionSetResource; /** @@ -181,7 +182,8 @@ public class UserResource /** * Returns a resource which abstracts operations available on the overall - * permissions granted to the User represented by this UserResource. + * permissions granted directly to the User represented by this + * UserResource. * * @return * A resource which representing the permissions granted to the User @@ -192,4 +194,21 @@ public class UserResource return new PermissionSetResource(user); } + /** + * Returns a read-only view of the permissions effectively granted to this + * user, including permissions which may be inherited or implied. + * + * @return + * A read-only view of the permissions effectively granted to this + * user. + * + * @throws GuacamoleException + * If the effective permissions for this user cannot be retrieved. + */ + @GET + @Path("effectivePermissions") + public APIPermissionSet getEffectivePermissions() throws GuacamoleException { + return new APIPermissionSet(user.getEffectivePermissions()); + } + } diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js index 856220767..2220e4d08 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js @@ -199,7 +199,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i }); // Query the user's permissions for the current connection - permissionService.getPermissions($scope.selectedDataSource, authenticationService.getCurrentUsername()) + permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername()) .success(function permissionsReceived(permissions) { $scope.permissions = permissions; diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js index bafe5912c..f342f4d1d 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js @@ -134,7 +134,7 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' }); // Query the user's permissions for the current connection group - permissionService.getPermissions($scope.selectedDataSource, authenticationService.getCurrentUsername()) + permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername()) .success(function permissionsReceived(permissions) { $scope.permissions = permissions; diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js b/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js index c5329750e..0583a6918 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js @@ -175,7 +175,7 @@ angular.module('manage').controller('manageSharingProfileController', ['$scope', }); // Query the user's permissions for the current sharing profile - permissionService.getPermissions($scope.selectedDataSource, authenticationService.getCurrentUsername()) + permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername()) .success(function permissionsReceived(permissions) { $scope.permissions = permissions; diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js index dd6939138..48519d5ec 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js @@ -680,7 +680,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto // Query the user's permissions for the current user dataSourceService.apply( - permissionService.getPermissions, + permissionService.getEffectivePermissions, dataSources, currentUsername ) diff --git a/guacamole/src/main/webapp/app/navigation/services/userPageService.js b/guacamole/src/main/webapp/app/navigation/services/userPageService.js index 749564bd1..24d340bd5 100644 --- a/guacamole/src/main/webapp/app/navigation/services/userPageService.js +++ b/guacamole/src/main/webapp/app/navigation/services/userPageService.js @@ -329,7 +329,7 @@ angular.module('navigation').factory('userPageService', ['$injector', // Retrieve current permissions dataSourceService.apply( - permissionService.getPermissions, + permissionService.getEffectivePermissions, authenticationService.getAvailableDataSources(), authenticationService.getCurrentUsername() ) @@ -422,7 +422,7 @@ angular.module('navigation').factory('userPageService', ['$injector', // Retrieve current permissions dataSourceService.apply( - permissionService.getPermissions, + permissionService.getEffectivePermissions, authenticationService.getAvailableDataSources(), authenticationService.getCurrentUsername() ) diff --git a/guacamole/src/main/webapp/app/rest/services/permissionService.js b/guacamole/src/main/webapp/app/rest/services/permissionService.js index a80377566..f108dfae1 100644 --- a/guacamole/src/main/webapp/app/rest/services/permissionService.js +++ b/guacamole/src/main/webapp/app/rest/services/permissionService.js @@ -36,8 +36,11 @@ angular.module('rest').factory('permissionService', ['$injector', /** * Returns the URL for the REST resource most appropriate for accessing - * the permissions of the user having the given username. - * + * the effective permissions of the user having the given username. + * Effective permissions differ from the permissions returned via + * getPermissions() in that permissions which are not directly granted to + * the user are included. + * * It is important to note that a particular data source can authenticate * and provide permissions for a user, even if that user does not exist * within that data source (and thus cannot be found beneath @@ -56,7 +59,7 @@ angular.module('rest').factory('permissionService', ['$injector', * The URL for the REST resource representing the user having the given * username. */ - var getPermissionsResourceURL = function getPermissionsResourceURL(dataSource, username) { + var getEffectivePermissionsResourceURL = function getEffectivePermissionsResourceURL(dataSource, username) { // Create base URL for data source var base = 'api/session/data/' + encodeURIComponent(dataSource); @@ -65,19 +68,21 @@ angular.module('rest').factory('permissionService', ['$injector', // user actually existing (they may not). Access their permissions via // "self" rather than the collection of defined users. if (username === authenticationService.getCurrentUsername()) - return base + '/self/permissions'; + return base + '/self/effectivePermissions'; // Otherwise, the user must exist for their permissions to be // accessible. Use the collection of defined users. - return base + '/users/' + encodeURIComponent(username) + '/permissions'; + return base + '/users/' + encodeURIComponent(username) + '/effectivePermissions'; }; /** - * Makes a request to the REST API to get the list of permissions for a - * given user, returning a promise that provides an array of - * @link{Permission} objects if successful. - * + * Makes a request to the REST API to get the list of effective permissions + * for a given user, returning a promise that provides an array of + * @link{Permission} objects if successful. Effective permissions differ + * from the permissions returned via getPermissions() in that permissions + * which are not directly granted to the user are included. + * * @param {String} dataSource * The unique identifier of the data source containing the user whose * permissions should be retrieved. This identifier corresponds to an @@ -85,12 +90,12 @@ angular.module('rest').factory('permissionService', ['$injector', * * @param {String} userID * The ID of the user to retrieve the permissions for. - * + * * @returns {Promise.} * A promise which will resolve with a @link{PermissionSet} upon * success. */ - service.getPermissions = function getPermissions(dataSource, userID) { + service.getEffectivePermissions = function getEffectivePermissions(dataSource, userID) { // Build HTTP parameters set var httpParameters = { @@ -101,58 +106,89 @@ angular.module('rest').factory('permissionService', ['$injector', return $http({ cache : cacheService.users, method : 'GET', - url : getPermissionsResourceURL(dataSource, userID), + url : getEffectivePermissionsResourceURL(dataSource, userID), params : httpParameters }); }; /** - * Makes a request to the REST API to add permissions for a given user, - * returning a promise that can be used for processing the results of the - * call. + * Returns the URL for the REST resource most appropriate for accessing + * the permissions of the user having the given identifier. The permissions + * retrieved differ from effective permissions (those returned by + * getEffectivePermissions()) in that only permissions which are directly + * granted to the user are included. * + * It is important to note that a particular data source can authenticate + * and provide permissions for a user, even if that user does not exist + * within that data source (and thus cannot be found beneath + * "api/session/data/{dataSource}/users") + * * @param {String} dataSource * The unique identifier of the data source containing the user whose - * permissions should be modified. This identifier corresponds to an + * permissions should be retrieved. This identifier corresponds to an * AuthenticationProvider within the Guacamole web application. * - * @param {String} userID - * The ID of the user to modify the permissions of. - * - * @param {PermissionSet} permissions - * The set of permissions to add. - * - * @returns {Promise} - * A promise for the HTTP call which will succeed if and only if the - * add operation is successful. + * @param {String} identifier + * The identifier of the user for which the URL of the proper REST + * resource should be derived. + * + * @returns {String} + * The URL for the REST resource representing the user having the given + * identifier. */ - service.addPermissions = function addPermissions(dataSource, userID, permissions) { - return service.patchPermissions(dataSource, userID, permissions, null); + var getPermissionsResourceURL = function getPermissionsResourceURL(dataSource, identifier) { + + // Create base URL for data source + var base = 'api/session/data/' + encodeURIComponent(dataSource); + + // If the username is that of the current user, do not rely on the + // user actually existing (they may not). Access their permissions via + // "self" rather than the collection of defined users. + if (identifier === authenticationService.getCurrentUsername()) + return base + '/self/permissions'; + + // Otherwise, the user must exist for their permissions to be + // accessible. Use the collection of defined users. + return base + '/users/' + encodeURIComponent(identifier) + '/permissions'; + }; - + /** - * Makes a request to the REST API to remove permissions for a given user, - * returning a promise that can be used for processing the results of the - * call. + * Makes a request to the REST API to get the list of permissions for a + * given user, returning a promise that provides an array of + * @link{Permission} objects if successful. The permissions retrieved + * differ from effective permissions (those returned by + * getEffectivePermissions()) in that only permissions which are directly + * granted to the user included. * * @param {String} dataSource * The unique identifier of the data source containing the user whose - * permissions should be modified. This identifier corresponds to an + * permissions should be retrieved. This identifier corresponds to an * AuthenticationProvider within the Guacamole web application. * - * @param {String} userID - * The ID of the user to modify the permissions of. - * - * @param {PermissionSet} permissions - * The set of permissions to remove. - * - * @returns {Promise} - * A promise for the HTTP call which will succeed if and only if the - * remove operation is successful. + * @param {String} identifier + * The identifier of the user to retrieve the permissions for. + * + * @returns {Promise.} + * A promise which will resolve with a @link{PermissionSet} upon + * success. */ - service.removePermissions = function removePermissions(dataSource, userID, permissions) { - return service.patchPermissions(dataSource, userID, null, permissions); + service.getPermissions = function getPermissions(dataSource, identifier) { + + // Build HTTP parameters set + var httpParameters = { + token : authenticationService.getCurrentToken() + }; + + // Retrieve user permissions + return $http({ + cache : cacheService.users, + method : 'GET', + url : getPermissionsResourceURL(dataSource, identifier), + params : httpParameters + }); + }; /** @@ -240,27 +276,30 @@ angular.module('rest').factory('permissionService', ['$injector', /** * Makes a request to the REST API to modify the permissions for a given * user, returning a promise that can be used for processing the results of - * the call. + * the call. This request affects only the permissions directly granted to + * the user, and may not affect permissions inherited through other means + * (effective permissions). * * @param {String} dataSource * The unique identifier of the data source containing the user whose * permissions should be modified. This identifier corresponds to an * AuthenticationProvider within the Guacamole web application. * - * @param {String} userID - * The ID of the user to modify the permissions of. + * @param {String} identifier + * The identifier of the user to modify the permissions of. * * @param {PermissionSet} [permissionsToAdd] * The set of permissions to add, if any. * * @param {PermissionSet} [permissionsToRemove] * The set of permissions to remove, if any. - * + * * @returns {Promise} * A promise for the HTTP call which will succeed if and only if the * patch operation is successful. */ - service.patchPermissions = function patchPermissions(dataSource, userID, permissionsToAdd, permissionsToRemove) { + service.patchPermissions = function patchPermissions(dataSource, identifier, + permissionsToAdd, permissionsToRemove) { var permissionPatch = []; @@ -278,7 +317,7 @@ angular.module('rest').factory('permissionService', ['$injector', // Patch user permissions return $http({ method : 'PATCH', - url : getPermissionsResourceURL(dataSource, userID), + url : getPermissionsResourceURL(dataSource, identifier), params : httpParameters, data : permissionPatch }) diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js index 6616853ee..a245fb2d1 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js @@ -404,7 +404,7 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe }; // Retrieve current permissions - permissionService.getPermissions($scope.dataSource, currentUsername) + permissionService.getEffectivePermissions($scope.dataSource, currentUsername) .success(function permissionsRetrieved(permissions) { // Store retrieved permissions diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js index d906c3dc0..655f23385 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js @@ -185,7 +185,7 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe }); // Retrieve current permissions - permissionService.getPermissions(dataSource, username) + permissionService.getEffectivePermissions(dataSource, username) .success(function permissionsRetrieved(permissions) { // Add action for changing password if permission is granted diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js index 1ac9fad9c..c14339990 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js @@ -232,7 +232,7 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings // Retrieve current permissions dataSourceService.apply( - permissionService.getPermissions, + permissionService.getEffectivePermissions, dataSources, currentUsername )