From c5c2984151cc63ab278eb0884a225fd785c9d03f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 8 Apr 2018 00:08:36 -0700 Subject: [PATCH] GUACAMOLE-220: Map and query user group tables. --- .../JDBCAuthenticationProviderModule.java | 14 ++ .../auth/jdbc/base/ModeledPermissions.java | 15 +- .../permission/UserGroupPermissionMapper.java | 25 ++ .../UserGroupPermissionService.java | 67 +++++ .../permission/UserGroupPermissionSet.java | 42 ++++ .../auth/jdbc/user/ModeledUserContext.java | 16 +- .../auth/jdbc/usergroup/ModeledUserGroup.java | 191 +++++++++++++++ .../jdbc/usergroup/UserGroupDirectory.java | 82 +++++++ .../auth/jdbc/usergroup/UserGroupMapper.java | 42 ++++ .../auth/jdbc/usergroup/UserGroupModel.java | 68 ++++++ .../auth/jdbc/usergroup/UserGroupService.java | 189 +++++++++++++++ .../permission/UserGroupPermissionMapper.xml | 156 ++++++++++++ .../auth/jdbc/usergroup/UserGroupMapper.xml | 229 ++++++++++++++++++ 13 files changed, 1127 insertions(+), 9 deletions(-) create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionMapper.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionService.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionSet.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/ModeledUserGroup.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupDirectory.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMapper.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupModel.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupService.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionMapper.xml create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMapper.xml diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java index 48c95c77b..b97e7e459 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java @@ -65,6 +65,9 @@ import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper; import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionMapper; import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionService; import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionSet; +import org.apache.guacamole.auth.jdbc.permission.UserGroupPermissionMapper; +import org.apache.guacamole.auth.jdbc.permission.UserGroupPermissionService; +import org.apache.guacamole.auth.jdbc.permission.UserGroupPermissionSet; import org.apache.guacamole.auth.jdbc.security.PasswordPolicyService; import org.apache.guacamole.auth.jdbc.sharing.ConnectionSharingService; import org.apache.guacamole.auth.jdbc.sharing.HashSharedConnectionMap; @@ -79,6 +82,10 @@ import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileService; import org.apache.guacamole.auth.jdbc.tunnel.RestrictedGuacamoleTunnelService; import org.apache.guacamole.auth.jdbc.user.PasswordRecordMapper; import org.apache.guacamole.auth.jdbc.user.UserRecordMapper; +import org.apache.guacamole.auth.jdbc.usergroup.ModeledUserGroup; +import org.apache.guacamole.auth.jdbc.usergroup.UserGroupDirectory; +import org.apache.guacamole.auth.jdbc.usergroup.UserGroupMapper; +import org.apache.guacamole.auth.jdbc.usergroup.UserGroupService; import org.mybatis.guice.MyBatisModule; import org.mybatis.guice.datasource.builtin.PooledDataSourceProvider; @@ -128,6 +135,8 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule { addMapperClass(SharingProfileMapper.class); addMapperClass(SharingProfileParameterMapper.class); addMapperClass(SharingProfilePermissionMapper.class); + addMapperClass(UserGroupMapper.class); + addMapperClass(UserGroupPermissionMapper.class); addMapperClass(UserMapper.class); addMapperClass(UserPermissionMapper.class); addMapperClass(UserRecordMapper.class); @@ -146,12 +155,15 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule { bind(ModeledSharingProfile.class); bind(ModeledUser.class); bind(ModeledUserContext.class); + bind(ModeledUserGroup.class); bind(RootConnectionGroup.class); bind(SharingProfileDirectory.class); bind(SharingProfilePermissionSet.class); bind(SystemPermissionSet.class); bind(TrackedActiveConnection.class); bind(UserDirectory.class); + bind(UserGroupDirectory.class); + bind(UserGroupPermissionSet.class); bind(UserPermissionSet.class); // Bind services @@ -172,6 +184,8 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule { bind(SharingProfilePermissionService.class); bind(SharingProfileService.class); bind(SystemPermissionService.class); + bind(UserGroupService.class); + bind(UserGroupPermissionService.class); bind(UserPermissionService.class); bind(UserService.class); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledPermissions.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledPermissions.java index 2f7808d2c..cda6f6aa8 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledPermissions.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledPermissions.java @@ -28,13 +28,13 @@ import org.apache.guacamole.auth.jdbc.activeconnection.ActiveConnectionPermissio import org.apache.guacamole.auth.jdbc.permission.ConnectionGroupPermissionService; import org.apache.guacamole.auth.jdbc.permission.ConnectionPermissionService; import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionService; +import org.apache.guacamole.auth.jdbc.permission.UserGroupPermissionService; import org.apache.guacamole.auth.jdbc.permission.UserPermissionService; import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.net.auth.Permissions; 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; /** * An implementation of the base Permissions interface which is common to both @@ -88,6 +88,12 @@ public abstract class ModeledPermissions @Inject private UserPermissionService userPermissionService; + /** + * Service for retrieving user group permissions. + */ + @Inject + private UserGroupPermissionService userGroupPermissionService; + /** * Returns whether the underlying entity is a user. Entities may be either * users or user groups. @@ -171,8 +177,8 @@ public abstract class ModeledPermissions @Override public ObjectPermissionSet getUserGroupPermissions() throws GuacamoleException { - // FIXME: STUB - return new SimpleObjectPermissionSet(); + return userGroupPermissionService.getPermissionSet(getCurrentUser(), + this, Collections.emptySet()); } /** @@ -256,8 +262,7 @@ public abstract class ModeledPermissions @Override public ObjectPermissionSet getUserGroupPermissions() throws GuacamoleException { - // FIXME: STUB - return new SimpleObjectPermissionSet(); + return userGroupPermissionService.getPermissionSet(getCurrentUser(), ModeledPermissions.this, effectiveGroups); } }; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionMapper.java new file mode 100644 index 000000000..d2a7494e2 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionMapper.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. + */ + +package org.apache.guacamole.auth.jdbc.permission; + +/** + * Mapper for user group permissions. + */ +public interface UserGroupPermissionMapper extends ObjectPermissionMapper {} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionService.java new file mode 100644 index 000000000..852b9f04c --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionService.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.jdbc.permission; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.Set; +import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.base.EntityModel; +import org.apache.guacamole.auth.jdbc.base.ModeledPermissions; + +/** + * Service which provides convenience methods for creating, retrieving, and + * deleting user group permissions. This service will automatically enforce the + * permissions of the current user. + */ +public class UserGroupPermissionService extends ModeledObjectPermissionService { + + /** + * Mapper for user group permissions. + */ + @Inject + private UserGroupPermissionMapper userGroupPermissionMapper; + + /** + * Provider for user group permission sets. + */ + @Inject + private Provider userGroupPermissionSetProvider; + + @Override + protected ObjectPermissionMapper getPermissionMapper() { + return userGroupPermissionMapper; + } + + @Override + public ObjectPermissionSet getPermissionSet(ModeledAuthenticatedUser user, + ModeledPermissions targetEntity, + Set effectiveGroups) throws GuacamoleException { + + // Create permission set for requested entity + ObjectPermissionSet permissionSet = userGroupPermissionSetProvider.get(); + permissionSet.init(user, targetEntity, effectiveGroups); + + return permissionSet; + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionSet.java new file mode 100644 index 000000000..a864144a2 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionSet.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.jdbc.permission; + +import com.google.inject.Inject; + +/** + * A database implementation of ObjectPermissionSet which uses an injected + * service to query and manipulate the user group permissions associated with a + * particular user. + */ +public class UserGroupPermissionSet extends ObjectPermissionSet { + + /** + * Service for querying and manipulating user group permissions. + */ + @Inject + private UserGroupPermissionService userGroupPermissionService; + + @Override + protected ObjectPermissionService getObjectPermissionService() { + return userGroupPermissionService; + } + +} 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 d53164b36..e98a25a04 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,7 +26,6 @@ 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; @@ -37,6 +36,8 @@ import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileDirectory; +import org.apache.guacamole.auth.jdbc.usergroup.ModeledUserGroup; +import org.apache.guacamole.auth.jdbc.usergroup.UserGroupDirectory; import org.apache.guacamole.form.Form; import org.apache.guacamole.net.auth.ActiveConnection; import org.apache.guacamole.net.auth.ActivityRecord; @@ -48,7 +49,6 @@ 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 @@ -63,6 +63,13 @@ public class ModeledUserContext extends RestrictedObject */ @Inject private UserDirectory userDirectory; + + /** + * User group directory restricted by the permissions of the user associated + * with this context. + */ + @Inject + private UserGroupDirectory userGroupDirectory; /** * Connection directory restricted by the permissions of the user @@ -128,6 +135,7 @@ public class ModeledUserContext extends RestrictedObject // Init directories userDirectory.init(currentUser); + userGroupDirectory.init(currentUser); connectionDirectory.init(currentUser); connectionGroupDirectory.init(currentUser); sharingProfileDirectory.init(currentUser); @@ -166,7 +174,7 @@ public class ModeledUserContext extends RestrictedObject @Override public Directory getUserGroupDirectory() throws GuacamoleException { - return new SimpleDirectory(); + return userGroupDirectory; } @Override @@ -224,7 +232,7 @@ public class ModeledUserContext extends RestrictedObject @Override public Collection
getUserGroupAttributes() { - return Collections.emptyList(); + return ModeledUserGroup.ATTRIBUTES; } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/ModeledUserGroup.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/ModeledUserGroup.java new file mode 100644 index 000000000..470bfab54 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/ModeledUserGroup.java @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.jdbc.usergroup; + +import com.google.inject.Inject; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.base.ModeledPermissions; +import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; +import org.apache.guacamole.form.BooleanField; +import org.apache.guacamole.form.Field; +import org.apache.guacamole.form.Form; +import org.apache.guacamole.net.auth.RelatedObjectSet; +import org.apache.guacamole.net.auth.UserGroup; +import org.apache.guacamole.net.auth.simple.SimpleRelatedObjectSet; + +/** + * An implementation of the UserGroup object which is backed by a database model. + */ +public class ModeledUserGroup extends ModeledPermissions + implements UserGroup { + + /** + * The name of the attribute which controls whether a user group is + * disabled. + */ + public static final String DISABLED_ATTRIBUTE_NAME = "disabled"; + + /** + * All attributes related to restricting user groups, within a logical + * form. + */ + public static final Form ACCOUNT_RESTRICTIONS = new Form("restrictions", Arrays.asList( + new BooleanField(DISABLED_ATTRIBUTE_NAME, "true") + )); + + /** + * All possible attributes of user groups organized as individual, + * logical forms. + */ + public static final Collection ATTRIBUTES = Collections.unmodifiableCollection(Arrays.asList( + ACCOUNT_RESTRICTIONS + )); + + /** + * The names of all attributes which are explicitly supported by this + * extension's UserGroup objects. + */ + public static final Set ATTRIBUTE_NAMES = + Collections.unmodifiableSet(new HashSet(Arrays.asList( + DISABLED_ATTRIBUTE_NAME + ))); + + /** + * Service for managing user groups. + */ + @Inject + private UserGroupService userGroupService; + + /** + * Whether attributes which control access restrictions should be exposed + * via getAttributes() or allowed to be set via setAttributes(). + */ + private boolean exposeRestrictedAttributes = false; + + /** + * Initializes this ModeledUserGroup, associating it with the current + * authenticated user and populating it with data from the given user group + * model. + * + * @param currentUser + * The user that created or retrieved this object. + * + * @param model + * The backing model object. + * + * @param exposeRestrictedAttributes + * Whether attributes which control access restrictions should be + * exposed via getAttributes() or allowed to be set via + * setAttributes(). + */ + public void init(ModeledAuthenticatedUser currentUser, UserGroupModel model, + boolean exposeRestrictedAttributes) { + super.init(currentUser, model); + this.exposeRestrictedAttributes = exposeRestrictedAttributes; + } + + /** + * Creates a new, empty ModeledUserGroup. + */ + public ModeledUserGroup() { + } + + /** + * Stores all restricted (privileged) attributes within the given Map, + * pulling the values of those attributes from the underlying user group + * model. If no value is yet defined for an attribute, that attribute will + * be set to null. + * + * @param attributes + * The Map to store all restricted attributes within. + */ + private void putRestrictedAttributes(Map attributes) { + + // Set disabled attribute + attributes.put(DISABLED_ATTRIBUTE_NAME, getModel().isDisabled() ? "true" : null); + + } + + /** + * Stores all restricted (privileged) attributes within the underlying user + * group model, pulling the values of those attributes from the given Map. + * + * @param attributes + * The Map to pull all restricted attributes from. + */ + private void setRestrictedAttributes(Map attributes) { + + // Translate disabled attribute + getModel().setDisabled("true".equals(attributes.get(DISABLED_ATTRIBUTE_NAME))); + + } + + @Override + public Set getSupportedAttributeNames() { + return ATTRIBUTE_NAMES; + } + + @Override + public Map getAttributes() { + + // Include any defined arbitrary attributes + Map attributes = super.getAttributes(); + + // Include restricted attributes only if they should be exposed + if (exposeRestrictedAttributes) + putRestrictedAttributes(attributes); + + return attributes; + } + + @Override + public void setAttributes(Map attributes) { + + // Set arbitrary attributes + super.setAttributes(attributes); + + // Assign restricted attributes only if they are exposed + if (exposeRestrictedAttributes) + setRestrictedAttributes(attributes); + + } + + @Override + public RelatedObjectSet getUserGroups() throws GuacamoleException { + return new SimpleRelatedObjectSet(); + } + + @Override + public RelatedObjectSet getMemberUsers() throws GuacamoleException { + return new SimpleRelatedObjectSet(); + } + + @Override + public RelatedObjectSet getMemberUserGroups() throws GuacamoleException { + return new SimpleRelatedObjectSet(); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupDirectory.java new file mode 100644 index 000000000..911b8521f --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupDirectory.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.auth.jdbc.usergroup; + +import com.google.inject.Inject; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.base.RestrictedObject; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.UserGroup; +import org.mybatis.guice.transactional.Transactional; + +/** + * Implementation of the UserGroup Directory which is driven by an underlying, + * arbitrary database. + */ +public class UserGroupDirectory extends RestrictedObject + implements Directory { + + /** + * Service for managing user group objects. + */ + @Inject + private UserGroupService userGroupService; + + @Override + public UserGroup get(String identifier) throws GuacamoleException { + return userGroupService.retrieveObject(getCurrentUser(), identifier); + } + + @Override + @Transactional + public Collection getAll(Collection identifiers) throws GuacamoleException { + Collection objects = userGroupService.retrieveObjects(getCurrentUser(), identifiers); + return Collections.unmodifiableCollection(objects); + } + + @Override + @Transactional + public Set getIdentifiers() throws GuacamoleException { + return userGroupService.getIdentifiers(getCurrentUser()); + } + + @Override + @Transactional + public void add(UserGroup object) throws GuacamoleException { + userGroupService.createObject(getCurrentUser(), object); + } + + @Override + @Transactional + public void update(UserGroup object) throws GuacamoleException { + ModeledUserGroup group = (ModeledUserGroup) object; + userGroupService.updateObject(getCurrentUser(), group); + } + + @Override + @Transactional + public void remove(String identifier) throws GuacamoleException { + userGroupService.deleteObject(getCurrentUser(), identifier); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMapper.java new file mode 100644 index 000000000..7c048f7f5 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMapper.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.jdbc.usergroup; + +import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; +import org.apache.ibatis.annotations.Param; + +/** + * Mapper for user group objects. + */ +public interface UserGroupMapper extends ModeledDirectoryObjectMapper { + + /** + * Returns the group having the given name, if any. If no such group + * exists, null is returned. + * + * @param name + * The name of the group to return. + * + * @return + * The group having the given name, or null if no such group exists. + */ + UserGroupModel selectOne(@Param("name") String name); + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupModel.java new file mode 100644 index 000000000..7fb9c8c54 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupModel.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.jdbc.usergroup; + +import org.apache.guacamole.auth.jdbc.base.EntityModel; +import org.apache.guacamole.auth.jdbc.base.EntityType; + +/** + * Object representation of a Guacamole user group, as represented in the + * database. + */ +public class UserGroupModel extends EntityModel { + + /** + * Whether the user group is disabled. Disabled accounts exist and can + * be modified, but cannot be used. + */ + private boolean disabled; + + /** + * Creates a new, empty user group. + */ + public UserGroupModel() { + super(EntityType.USER_GROUP); + } + + /** + * Returns whether this user group has been disabled. Memberships of + * disabled user groups are treated as non-existent, effectively disabling + * membership in that group. + * + * @return + * true if this user group is disabled, false otherwise. + */ + public boolean isDisabled() { + return disabled; + } + + /** + * Sets whether this user group has been disabled. Memberships of disabled + * user groups are treated as non-existent, effectively disabling + * membership in that group. + * + * @param disabled + * true if this user group should be disabled, false otherwise. + */ + public void setDisabled(boolean disabled) { + this.disabled = disabled; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupService.java new file mode 100644 index 000000000..dfe1ef560 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupService.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.jdbc.usergroup; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.Collection; +import java.util.Collections; +import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; +import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectService; +import org.apache.guacamole.GuacamoleClientException; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.base.EntityMapper; +import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionMapper; +import org.apache.guacamole.auth.jdbc.permission.UserGroupPermissionMapper; +import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; +import org.apache.guacamole.net.auth.UserGroup; +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; + +/** + * Service which provides convenience methods for creating, retrieving, and + * manipulating user groups. + */ +public class UserGroupService extends ModeledDirectoryObjectService { + + /** + * Mapper for creating/deleting entities. + */ + @Inject + private EntityMapper entityMapper; + + /** + * Mapper for accessing user groups. + */ + @Inject + private UserGroupMapper userGroupMapper; + + /** + * Mapper for manipulating user group permissions. + */ + @Inject + private UserGroupPermissionMapper userGroupPermissionMapper; + + /** + * Provider for creating user groups. + */ + @Inject + private Provider userGroupProvider; + + @Override + protected ModeledDirectoryObjectMapper getObjectMapper() { + return userGroupMapper; + } + + @Override + protected ObjectPermissionMapper getPermissionMapper() { + return userGroupPermissionMapper; + } + + @Override + protected ModeledUserGroup getObjectInstance(ModeledAuthenticatedUser currentUser, + UserGroupModel model) throws GuacamoleException { + + boolean exposeRestrictedAttributes; + + // Expose restricted attributes if the user group does not yet exist + if (model.getObjectID() == null) + exposeRestrictedAttributes = true; + + // Otherwise, expose restricted attributes only if the user has + // ADMINISTER permission + else + exposeRestrictedAttributes = hasObjectPermission(currentUser, + model.getIdentifier(), ObjectPermission.Type.ADMINISTER); + + // Produce ModeledUserGroup exposing only those attributes for which the + // current user has permission + ModeledUserGroup group = userGroupProvider.get(); + group.init(currentUser, model, exposeRestrictedAttributes); + return group; + + } + + @Override + protected UserGroupModel getModelInstance(ModeledAuthenticatedUser currentUser, + final UserGroup object) throws GuacamoleException { + + // Create new ModeledUserGroup backed by blank model + UserGroupModel model = new UserGroupModel(); + ModeledUserGroup group = getObjectInstance(currentUser, model); + + // Set model contents through ModeledUser, copying the provided group + group.setIdentifier(object.getIdentifier()); + group.setAttributes(object.getAttributes()); + + return model; + + } + + @Override + protected boolean hasCreatePermission(ModeledAuthenticatedUser user) + throws GuacamoleException { + + // Return whether user has explicit user group creation permission + SystemPermissionSet permissionSet = user.getUser().getEffectivePermissions().getSystemPermissions(); + return permissionSet.hasPermission(SystemPermission.Type.CREATE_USER); + + } + + @Override + protected ObjectPermissionSet getEffectivePermissionSet(ModeledAuthenticatedUser user) + throws GuacamoleException { + + // Return permissions related to user groups + return user.getUser().getEffectivePermissions().getUserGroupPermissions(); + + } + + @Override + protected void beforeCreate(ModeledAuthenticatedUser user, UserGroup object, + UserGroupModel model) throws GuacamoleException { + + super.beforeCreate(user, object, model); + + // Group name must not be blank + if (model.getIdentifier() == null || model.getIdentifier().trim().isEmpty()) + throw new GuacamoleClientException("The group name must not be blank."); + + // Do not create duplicate user groups + Collection existing = userGroupMapper.select(Collections.singleton(model.getIdentifier())); + if (!existing.isEmpty()) + throw new GuacamoleClientException("Group \"" + model.getIdentifier() + "\" already exists."); + + // Create base entity object, implicitly populating underlying entity ID + entityMapper.insert(model); + + } + + @Override + protected void beforeUpdate(ModeledAuthenticatedUser user, + ModeledUserGroup object, UserGroupModel model) throws GuacamoleException { + + super.beforeUpdate(user, object, model); + + // Username must not be blank + if (model.getIdentifier() == null || model.getIdentifier().trim().isEmpty()) + throw new GuacamoleClientException("The group name must not be blank."); + + // Check whether such a group is already present + UserGroupModel existing = userGroupMapper.selectOne(model.getIdentifier()); + if (existing != null) { + + // Do not rename to existing user group + if (!existing.getObjectID().equals(model.getObjectID())) + throw new GuacamoleClientException("Group \"" + model.getIdentifier() + "\" already exists."); + + } + + } + + @Override + protected boolean isValidIdentifier(String identifier) { + + // All strings are valid group identifiers + return true; + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionMapper.xml new file mode 100644 index 000000000..44ac20170 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserGroupPermissionMapper.xml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM guacamole_user_group_permission + USING guacamole_user_group affected_group, guacamole_entity affected_entity + WHERE + guacamole_user_group_permission.affected_user_group_id = affected_group.user_group_id + AND affected_group.entity_id = affected_entity.entity_id + AND (guacamole_user_group_permission.entity_id, permission, affected_entity.name) IN + + (#{permission.entityID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}::guacamole_object_permission_type, + #{permission.objectIdentifier,jdbcType=INTEGER}) + + AND affected_entity.type = 'USER_GROUP'::guacamole_entity_type + + + + + + + INSERT INTO guacamole_user_group_permission ( + entity_id, + permission, + affected_user_group_id + ) + SELECT DISTINCT + permissions.entity_id, + permissions.permission, + affected_group.user_group_id + FROM + + SELECT #{permission.entityID,jdbcType=INTEGER} AS entity_id, + #{permission.type,jdbcType=VARCHAR}::guacamole_object_permission_type AS permission, + #{permission.objectIdentifier,jdbcType=VARCHAR}::text AS affected_name + + AS permissions + JOIN guacamole_entity affected_entity ON + affected_entity.name = permissions.affected_name + AND affected_entity.type = 'USER_GROUP'::guacamole_entity_type + JOIN guacamole_user_group affected_group ON affected_group.entity_id = affected_entity.entity_id + WHERE (permissions.entity_id, permissions.permission, affected_group.user_group_id) NOT IN ( + SELECT + guacamole_user_group_permission.entity_id, + guacamole_user_group_permission.permission, + guacamole_user_group_permission.affected_user_group_id + FROM guacamole_user_group_permission + ); + + + + \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMapper.xml new file mode 100644 index 000000000..0006d4267 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMapper.xml @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM guacamole_entity + WHERE + name = #{identifier,jdbcType=VARCHAR} + AND type = 'USER_GROUP'::guacamole_entity_type + + + + + + INSERT INTO guacamole_user_group ( + entity_id, + disabled + ) + VALUES ( + #{object.entityID,jdbcType=VARCHAR}, + #{object.disabled,jdbcType=BOOLEAN} + ) + + + + + + UPDATE guacamole_user_group + SET disabled = #{object.disabled,jdbcType=BOOLEAN} + WHERE user_group_id = #{object.objectID,jdbcType=VARCHAR} + + + + + DELETE FROM guacamole_user_group_attribute + WHERE user_group_id = #{object.objectID,jdbcType=INTEGER} + + + + + INSERT INTO guacamole_user_group_attribute ( + user_group_id, + attribute_name, + attribute_value + ) + VALUES + + (#{object.objectID,jdbcType=INTEGER}, + #{attribute.name,jdbcType=VARCHAR}, + #{attribute.value,jdbcType=VARCHAR}) + + + +