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.");
+
+    }
+
+}

Reply via email to