]> BookStack Code Mirror - bookstack/commitdiff
Created big scary query to apply permissions via new format
authorDan Brown <redacted>
Thu, 22 Dec 2022 20:32:06 +0000 (20:32 +0000)
committerDan Brown <redacted>
Thu, 22 Dec 2022 20:32:06 +0000 (20:32 +0000)
app/Auth/Permissions/JointPermissionBuilder.php
app/Auth/Permissions/PermissionApplicator.php
app/Entities/Models/Entity.php
dev/docs/permission-scenario-testing.md

index 4d8692aab6d296ac1519605a9e86ddd4b46f90c1..313f5de18638d7b33ffb7b6b10c8efdf135ccd68 100644 (file)
@@ -2,7 +2,6 @@
 
 namespace BookStack\Auth\Permissions;
 
-use BookStack\Auth\Role;
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Models\BookChild;
 use BookStack\Entities\Models\Bookshelf;
index 58ee7d93a1db4a58ac3da711bb41b1672e61827a..ee2027e8f33ff261d8c39c69cbd13c07d186ed85 100644 (file)
@@ -183,20 +183,70 @@ class PermissionApplicator
      * Limit the given entity query so that the query will only
      * return items that the user has view permission for.
      */
-    public function restrictEntityQuery(Builder $query): Builder
+    public function restrictEntityQuery(Builder $query, string $morphClass): Builder
     {
-        return $query->where(function (Builder $parentQuery) {
-            $parentQuery->whereHas('jointPermissions', function (Builder $permissionQuery) {
-                $permissionQuery->whereIn('role_id', $this->getCurrentUserRoleIds())
-                    ->where(function (Builder $query) {
-                        $this->addJointHasPermissionCheck($query, $this->currentUser()->id);
-                    });
-            })->orWhereHas('jointUserPermissions', function (Builder $query) {
-                $query->where('user_id', '=', $this->currentUser()->id)->where('has_permission', '=', true);
-            });
-        })->whereDoesntHave('jointUserPermissions', function (Builder $query) {
-            $query->where('user_id', '=', $this->currentUser()->id)->where('has_permission', '=', false);
+        $this->getCurrentUserRoleIds();
+        $this->currentUser()->id;
+
+        $userViewAll = userCan($morphClass . '-view-all');
+        $userViewOwn = userCan($morphClass . '-view-own');
+
+        // TODO - Leave this as the new admin workaround?
+        //   Or auto generate collapsed role permissions for admins?
+        if (\user()->hasSystemRole('admin')) {
+            return $query;
+        }
+
+        // Fallback permission join
+        $query->joinSub(function (QueryBuilder $joinQuery) use ($morphClass) {
+            $joinQuery->select(['entity_id'])->selectRaw('max(view) as perms_fallback')
+                ->from('entity_permissions_collapsed')
+                ->where('entity_type', '=', $morphClass)
+                ->whereNull(['role_id', 'user_id'])
+                ->groupBy('entity_id');
+        }, 'p_f', 'id', '=', 'p_f.entity_id', 'left');
+
+        // Role permission join
+        $query->joinSub(function (QueryBuilder $joinQuery) use ($morphClass) {
+            $joinQuery->select(['entity_id'])->selectRaw('max(view) as perms_role')
+                ->from('entity_permissions_collapsed')
+                ->where('entity_type', '=', $morphClass)
+                ->whereIn('role_id', $this->getCurrentUserRoleIds())
+                ->groupBy('entity_id');
+        }, 'p_r', 'id', '=', 'p_r.entity_id', 'left');
+
+        // User permission join
+        $query->joinSub(function (QueryBuilder $joinQuery) use ($morphClass) {
+            $joinQuery->select(['entity_id'])->selectRaw('max(view) as perms_user')
+                ->from('entity_permissions_collapsed')
+                ->where('entity_type', '=', $morphClass)
+                ->where('user_id', '=', $this->currentUser()->id)
+                ->groupBy('entity_id');
+        }, 'p_u', 'id', '=', 'p_u.entity_id', 'left');
+
+        // Where permissions apply
+        $query->where(function (Builder $query) use ($userViewOwn, $userViewAll) {
+            $query->where('perms_user', '=', 1)
+                ->orWhere(function (Builder $query) {
+                    $query->whereNull('perms_user')->where('perms_role', '=', 1);
+                })->orWhere(function (Builder $query) {
+                    $query->whereNull(['perms_user', 'perms_role'])
+                        ->where('perms_fallback', '=', 1);
+                });
+
+            if ($userViewAll) {
+                $query->orWhere(function (Builder $query) {
+                    $query->whereNull(['perms_user', 'perms_role', 'perms_fallback']);
+                });
+            } else if ($userViewOwn) {
+                $query->orWhere(function (Builder $query) {
+                    $query->whereNull(['perms_user', 'perms_role', 'perms_fallback'])
+                        ->where('created_by', '=', $this->currentUser()->id);
+                });
+            }
         });
+
+        return $query;
     }
 
     /**
@@ -226,6 +276,9 @@ class PermissionApplicator
         $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn];
         $pageMorphClass = (new Page())->getMorphClass();
 
+        // TODO;
+        return $query;
+
         $q = $query->where(function ($query) use ($tableDetails) {
             $query->whereExists(function ($permissionQuery) use ($tableDetails) {
                 /** @var Builder $permissionQuery */
@@ -275,6 +328,9 @@ class PermissionApplicator
         $fullPageIdColumn = $tableName . '.' . $pageIdColumn;
         $morphClass = (new Page())->getMorphClass();
 
+        // TODO
+        return $query;
+
         $existsQuery = function ($permissionQuery) use ($fullPageIdColumn, $morphClass) {
             /** @var Builder $permissionQuery */
             $permissionQuery->select('joint_permissions.role_id')->from('joint_permissions')
index 4552b5659d3c8278328d74c337b4ccd3144f9e60..fc83bdd7eed6af9e371298087ee477d946618df2 100644 (file)
@@ -70,7 +70,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
      */
     public function scopeVisible(Builder $query): Builder
     {
-        return app()->make(PermissionApplicator::class)->restrictEntityQuery($query);
+        return app()->make(PermissionApplicator::class)->restrictEntityQuery($query, $this->getMorphClass());
     }
 
     /**
index bfb5e7aa309468639f58bb72760bd23981e5764d..6d0935f09ae0befc79ee0cf7a8205d01bd205062 100644 (file)
@@ -8,8 +8,11 @@ Tests are categorised by the most specific element involved in the scenario, whe
 
 - User entity permissions.
 - Role entity permissions.
+- Fallback entity permissions.
 - Role permissions.
 
+- TODO - Test fallback in the context of the above.
+
 ## General Permission Logical Rules
 
 The below are some general rules we follow to standardise the behaviour of permissions in the platform: