GUACAMOLE-220: Define base interfaces for mapping RelatedObjectSets to the database.
Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/8f06b7a3 Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/8f06b7a3 Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/8f06b7a3 Branch: refs/heads/staging/1.0.0 Commit: 8f06b7a3f9293254a546914dd403e322546fe03b Parents: a39d863 Author: Michael Jumper <mjum...@apache.org> Authored: Tue Apr 10 12:16:11 2018 -0700 Committer: Michael Jumper <mjum...@apache.org> Committed: Wed Sep 19 23:56:52 2018 -0700 ---------------------------------------------------------------------- .../auth/jdbc/base/ObjectRelationMapper.java | 126 +++++++++++ .../auth/jdbc/base/RelatedObjectSet.java | 211 +++++++++++++++++++ 2 files changed, 337 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/8f06b7a3/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ObjectRelationMapper.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ObjectRelationMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ObjectRelationMapper.java new file mode 100644 index 0000000..082db0f --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ObjectRelationMapper.java @@ -0,0 +1,126 @@ +/* + * 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.base; + +import java.util.Collection; +import java.util.Set; +import org.apache.guacamole.auth.jdbc.user.UserModel; +import org.apache.ibatis.annotations.Param; + +/** + * Mapper for the relations represented by a particular RelatedObjectSet + * implementation. + * + * @param <ParentModelType> + * The underlying database model of the object on the parent side of the + * one-to-many relationship represented by the RelatedObjectSet mapped by + * this ObjectRelationMapper. + */ +public interface ObjectRelationMapper<ParentModelType extends ObjectModel> { + + /** + * Inserts rows as necessary to establish the one-to-many relationship + * represented by the RelatedObjectSet between the given parent and + * children. If the relation for any parent/child pair is already present, + * no attempt is made to insert a new row for that relation. + * + * @param parent + * The model of the object on the parent side of the one-to-many + * relationship represented by the RelatedObjectSet. + * + * @param children + * The identifiers of the objects on the child side of the one-to-many + * relationship represented by the RelatedObjectSet. + * + * @return + * The number of rows inserted. + */ + int insert(@Param("parent") ParentModelType parent, + @Param("children") Collection<String> children); + + /** + * Deletes rows as necessary to establish the one-to-many relationship + * represented by the RelatedObjectSet between the given parent and + * children. If the relation for any parent/child pair does not exist, + * that specific relation is ignored, and deletion proceeds with the + * remaining relations. + * + * @param parent + * The model of the object on the parent side of the one-to-many + * relationship represented by the RelatedObjectSet. + * + * @param children + * The identifiers of the objects on the child side of the one-to-many + * relationship represented by the RelatedObjectSet. + * + * @return + * The number of rows deleted. + */ + int delete(@Param("parent") ParentModelType parent, + @Param("children") Collection<String> children); + + /** + * Retrieves the identifiers of all objects on the child side of the + * one-to-many relationship represented by the RelatedObjectSet mapped by + * this ObjectRelationMapper. This should only be called on behalf of a + * system administrator. If identifiers are needed by a non-administrative + * user who must have explicit read rights, use + * selectReadableChildIdentifiers() instead. + * + * @param parent + * The model of the object on the parent side of the one-to-many + * relationship represented by the RelatedObjectSet. + * + * @return + * A Set containing the identifiers of all objects on the child side + * of the one-to-many relationship. + */ + Set<String> selectChildIdentifiers(@Param("parent") ParentModelType parent); + + /** + * Retrieves the identifiers of all objects on the child side of the + * one-to-many relationship represented by the RelatedObjectSet mapped by + * this ObjectRelationMapper, including only those objects which are + * explicitly readable by the given user. If identifiers are needed by a + * system administrator (who, by definition, does not need explicit read + * rights), use selectChildIdentifiers() instead. + + * + * @param user + * The user whose permissions should determine whether an identifier + * is returned. + * + * @param effectiveGroups + * The identifiers of any known effective groups that should be taken + * into account, such as those defined externally to the database. + * + * @param parent + * The model of the object on the parent side of the one-to-many + * relationship represented by the RelatedObjectSet. + * + * @return + * A Set containing the identifiers of all readable objects on the + * child side of the one-to-many relationship. + */ + Set<String> selectReadableChildIdentifiers(@Param("user") UserModel user, + @Param("effectiveGroups") Collection<String> effectiveGroups, + @Param("parent") ParentModelType parent); + +} http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/8f06b7a3/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RelatedObjectSet.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RelatedObjectSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RelatedObjectSet.java new file mode 100644 index 0000000..a46cb27 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RelatedObjectSet.java @@ -0,0 +1,211 @@ +/* + * 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.base; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.net.auth.permission.ObjectPermission; +import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; + +/** + * A database implementation of RelatedObjectSet which provides access to a + * parent object and corresponding set of objects related to the parent, subject + * to object-level permissions. Though the parent and child objects have + * specific types, only the parent object's type is enforced through type + * parameters, as child objects are represented by identifiers only. + * + * @param <ParentObjectType> + * The type of object that represents the parent side of the relation. + * + * @param <ParentModelType> + * The underlying database model of the parent object. + */ +public abstract class RelatedObjectSet<ParentObjectType extends ModeledDirectoryObject<ParentModelType>, ParentModelType extends ObjectModel> + extends RestrictedObject implements org.apache.guacamole.net.auth.RelatedObjectSet { + + /** + * The parent object which shares some arbitrary relation with the objects + * within this set. + */ + private ParentObjectType parent; + + /** + * Creates a new RelatedObjectSet. The resulting object set must still be + * initialized by a call to init(). + */ + public RelatedObjectSet() { + } + + /** + * Initializes this RelatedObjectSet with the current user and the single + * object on the parent side of the one-to-many relation represented by the + * set. + * + * @param currentUser + * The user who queried this RelatedObjectSet, and whose permissions + * dictate the access level of all operations performed on this set. + * + * @param parent + * The parent object which shares some arbitrary relation with the + * objects within this set. + */ + public void init(ModeledAuthenticatedUser currentUser, ParentObjectType parent) { + super.init(currentUser); + this.parent = parent; + } + + /** + * Returns the mapper which provides low-level access to the the database + * models which drive the relation represented by this RelatedObjectSet. + * + * @return + * The mapper which provides low-level access to the the database + * models which drive the relation represented by this + * RelatedObjectSet. + */ + protected abstract ObjectRelationMapper<ParentModelType> getObjectRelationMapper(); + + /** + * Returns the permission set which exposes the effective permissions + * available to the current user regarding the objects on the parent side + * of the one-to-many relationship represented by this RelatedObjectSet. + * Permission inheritance through user groups is taken into account. + * + * @return + * The permission set which exposes the effective permissions + * available to the current user regarding the objects on the parent + * side of the one-to-many relationship represented by this + * RelatedObjectSet. + * + * @throws GuacamoleException + * If permission to query permission status is denied. + */ + protected abstract ObjectPermissionSet getParentObjectEffectivePermissionSet() + throws GuacamoleException; + + /** + * Returns the permission set which exposes the effective permissions + * available to the current user regarding the objects on the child side + * of the one-to-many relationship represented by this RelatedObjectSet. + * Permission inheritance through user groups is taken into account. + * + * @return + * The permission set which exposes the effective permissions + * available to the current user regarding the objects on the child + * side of the one-to-many relationship represented by this + * RelatedObjectSet. + * + * @throws GuacamoleException + * If permission to query permission status is denied. + */ + protected abstract ObjectPermissionSet getChildObjectEffectivePermissionSet() + throws GuacamoleException; + + /** + * Returns whether the current user has permission to alter that status of + * the relation between the parent object and the given child objects. + * + * @param identifiers + * The identifiers of all objects on the child side of the one-to-many + * relation being changed. + * + * @return + * true if the user has permission to make the described changes, + * false otherwise. + * + * @throws GuacamoleException + * If permission to query permission status is denied. + */ + private boolean canAlterRelation(Collection<String> identifiers) + throws GuacamoleException { + + // System administrators may alter any relations + if (getCurrentUser().getUser().isAdministrator()) + return true; + + // Non-admin users require UPDATE permission on the parent object ... + if (!getParentObjectEffectivePermissionSet().hasPermission( + ObjectPermission.Type.UPDATE, parent.getIdentifier())) + return false; + + // ... as well as UPDATE permission on all child objects being changed + Collection<String> accessibleIdentifiers = + getChildObjectEffectivePermissionSet().getAccessibleObjects( + Collections.singleton(ObjectPermission.Type.UPDATE), + identifiers); + + return accessibleIdentifiers.size() == identifiers.size(); + + } + + @Override + public Set<String> getObjects() throws GuacamoleException { + + // Bypass permission checks if the user is a system admin + ModeledAuthenticatedUser user = getCurrentUser(); + if (user.getUser().isAdministrator()) + return getObjectRelationMapper().selectChildIdentifiers(parent.getModel()); + + // Otherwise only return explicitly readable identifiers + return getObjectRelationMapper().selectReadableChildIdentifiers( + user.getUser().getModel(), user.getEffectiveUserGroups(), + parent.getModel()); + + } + + @Override + public void addObjects(Set<String> identifiers) throws GuacamoleException { + + // Nothing to do if nothing provided + if (identifiers.isEmpty()) + return; + + // Create relations only if permission is granted + if (canAlterRelation(identifiers)) + getObjectRelationMapper().insert(parent.getModel(), identifiers); + + // User lacks permission to add user groups + else + throw new GuacamoleSecurityException("Permission denied."); + + } + + @Override + public void removeObjects(Set<String> identifiers) throws GuacamoleException { + + // Nothing to do if nothing provided + if (identifiers.isEmpty()) + return; + + // Delete relations only if permission is granted + if (canAlterRelation(identifiers)) + getObjectRelationMapper().delete(parent.getModel(), identifiers); + + // User lacks permission to remove user groups + else + throw new GuacamoleSecurityException("Permission denied."); + + } + +}