X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/7973412c297720daa94d98980960d2b4ba3c9d17..refs/pull/270/head:/app/Services/PermissionService.php diff --git a/app/Services/PermissionService.php b/app/Services/PermissionService.php index cee074cd7..6363541ef 100644 --- a/app/Services/PermissionService.php +++ b/app/Services/PermissionService.php @@ -8,20 +8,24 @@ use BookStack\Ownable; use BookStack\Page; use BookStack\Role; use BookStack\User; +use Illuminate\Database\Connection; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; class PermissionService { - protected $userRoles; - protected $isAdmin; protected $currentAction; - protected $currentUser; + protected $isAdminUser; + protected $userRoles = false; + protected $currentUserModel = false; public $book; public $chapter; public $page; + protected $db; + protected $jointPermission; protected $role; @@ -30,24 +34,21 @@ class PermissionService /** * PermissionService constructor. * @param JointPermission $jointPermission + * @param Connection $db * @param Book $book * @param Chapter $chapter * @param Page $page * @param Role $role */ - public function __construct(JointPermission $jointPermission, Book $book, Chapter $chapter, Page $page, Role $role) + public function __construct(JointPermission $jointPermission, Connection $db, Book $book, Chapter $chapter, Page $page, Role $role) { - $this->currentUser = auth()->user(); - $userSet = $this->currentUser !== null; - $this->userRoles = false; - $this->isAdmin = $userSet ? $this->currentUser->hasRole('admin') : false; - if (!$userSet) $this->currentUser = new User(); - + $this->db = $db; $this->jointPermission = $jointPermission; $this->role = $role; $this->book = $book; $this->chapter = $chapter; $this->page = $page; + // TODO - Update so admin still goes through filters } /** @@ -117,7 +118,7 @@ class PermissionService } - foreach ($this->currentUser->roles as $role) { + foreach ($this->currentUser()->roles as $role) { $roles[] = $role->id; } return $roles; @@ -307,6 +308,10 @@ class PermissionService $explodedAction = explode('-', $action); $restrictionAction = end($explodedAction); + if ($role->system_name === 'admin') { + return $this->createJointPermissionDataArray($entity, $role, $action, true, true); + } + if ($entity->isA('book')) { if (!$entity->restricted) { @@ -389,21 +394,25 @@ class PermissionService */ public function checkOwnableUserAccess(Ownable $ownable, $permission) { - if ($this->isAdmin) return true; + if ($this->isAdmin()) { + $this->clean(); + return true; + } + $explodedPermission = explode('-', $permission); $baseQuery = $ownable->where('id', '=', $ownable->id); $action = end($explodedPermission); $this->currentAction = $action; - $nonJointPermissions = ['restrictions']; + $nonJointPermissions = ['restrictions', 'image', 'attachment']; // Handle non entity specific jointPermissions if (in_array($explodedPermission[0], $nonJointPermissions)) { - $allPermission = $this->currentUser && $this->currentUser->can($permission . '-all'); - $ownPermission = $this->currentUser && $this->currentUser->can($permission . '-own'); + $allPermission = $this->currentUser() && $this->currentUser()->can($permission . '-all'); + $ownPermission = $this->currentUser() && $this->currentUser()->can($permission . '-own'); $this->currentAction = 'view'; - $isOwner = $this->currentUser && $this->currentUser->id === $ownable->created_by; + $isOwner = $this->currentUser() && $this->currentUser()->id === $ownable->created_by; return ($allPermission || ($isOwner && $ownPermission)); } @@ -412,8 +421,9 @@ class PermissionService $this->currentAction = $permission; } - - return $this->entityRestrictionQuery($baseQuery)->count() > 0; + $q = $this->entityRestrictionQuery($baseQuery)->count() > 0; + $this->clean(); + return $q; } /** @@ -443,7 +453,7 @@ class PermissionService */ protected function entityRestrictionQuery($query) { - return $query->where(function ($parentQuery) { + $q = $query->where(function ($parentQuery) { $parentQuery->whereHas('jointPermissions', function ($permissionQuery) { $permissionQuery->whereIn('role_id', $this->getRoles()) ->where('action', '=', $this->currentAction) @@ -451,65 +461,72 @@ class PermissionService $query->where('has_permission', '=', true) ->orWhere(function ($query) { $query->where('has_permission_own', '=', true) - ->where('created_by', '=', $this->currentUser->id); + ->where('created_by', '=', $this->currentUser()->id); }); }); }); }); + $this->clean(); + return $q; } /** - * Add restrictions for a page query - * @param $query - * @param string $action - * @return mixed + * Get the children of a book in an efficient single query, Filtered by the permission system. + * @param integer $book_id + * @param bool $filterDrafts + * @return \Illuminate\Database\Query\Builder */ - public function enforcePageRestrictions($query, $action = 'view') - { - // Prevent drafts being visible to others. - $query = $query->where(function ($query) { - $query->where('draft', '=', false); - if ($this->currentUser) { - $query->orWhere(function ($query) { - $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser->id); + public function bookChildrenQuery($book_id, $filterDrafts = false) { + $pageSelect = $this->db->table('pages')->selectRaw("'BookStack\\\\Page' as entity_type, id, slug, name, text, '' as description, book_id, priority, chapter_id, draft")->where('book_id', '=', $book_id)->where(function($query) use ($filterDrafts) { + $query->where('draft', '=', 0); + if (!$filterDrafts) { + $query->orWhere(function($query) { + $query->where('draft', '=', 1)->where('created_by', '=', $this->currentUser()->id); }); } }); - - return $this->enforceEntityRestrictions($query, $action); + $chapterSelect = $this->db->table('chapters')->selectRaw("'BookStack\\\\Chapter' as entity_type, id, slug, name, '' as text, description, book_id, priority, 0 as chapter_id, 0 as draft")->where('book_id', '=', $book_id); + $whereQuery = $this->db->table('joint_permissions as jp')->selectRaw('COUNT(*)') + ->whereRaw('jp.entity_id=U.id')->whereRaw('jp.entity_type=U.entity_type') + ->where('jp.action', '=', 'view')->whereIn('jp.role_id', $this->getRoles()) + ->where(function($query) { + $query->where('jp.has_permission', '=', 1)->orWhere(function($query) { + $query->where('jp.has_permission_own', '=', 1)->where('jp.created_by', '=', $this->currentUser()->id); + }); + }); + $query = $this->db->query()->select('*')->from($this->db->raw("({$pageSelect->toSql()} UNION {$chapterSelect->toSql()}) AS U")) + ->mergeBindings($pageSelect)->mergeBindings($chapterSelect) + ->whereRaw("({$whereQuery->toSql()}) > 0")->mergeBindings($whereQuery)->orderBy('draft', 'desc')->orderBy('priority', 'asc'); + $this->clean(); + return $query; } /** - * Add on permission restrictions to a chapter query. - * @param $query + * Add restrictions for a generic entity + * @param string $entityType + * @param Builder|Entity $query * @param string $action * @return mixed */ - public function enforceChapterRestrictions($query, $action = 'view') + public function enforceEntityRestrictions($entityType, $query, $action = 'view') { - return $this->enforceEntityRestrictions($query, $action); - } + if (strtolower($entityType) === 'page') { + // Prevent drafts being visible to others. + $query = $query->where(function ($query) { + $query->where('draft', '=', false); + if ($this->currentUser()) { + $query->orWhere(function ($query) { + $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser()->id); + }); + } + }); + } - /** - * Add restrictions to a book query. - * @param $query - * @param string $action - * @return mixed - */ - public function enforceBookRestrictions($query, $action = 'view') - { - return $this->enforceEntityRestrictions($query, $action); - } + if ($this->isAdmin()) { + $this->clean(); + return $query; + } - /** - * Add restrictions for a generic entity - * @param $query - * @param string $action - * @return mixed - */ - public function enforceEntityRestrictions($query, $action = 'view') - { - if ($this->isAdmin) return $query; $this->currentAction = $action; return $this->entityRestrictionQuery($query); } @@ -524,11 +541,15 @@ class PermissionService */ public function filterRestrictedEntityRelations($query, $tableName, $entityIdColumn, $entityTypeColumn) { - if ($this->isAdmin) return $query; + if ($this->isAdmin()) { + $this->clean(); + return $query; + } + $this->currentAction = 'view'; $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn]; - return $query->where(function ($query) use ($tableDetails) { + $q = $query->where(function ($query) use ($tableDetails) { $query->whereExists(function ($permissionQuery) use (&$tableDetails) { $permissionQuery->select('id')->from('joint_permissions') ->whereRaw('joint_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn']) @@ -538,12 +559,12 @@ class PermissionService ->where(function ($query) { $query->where('has_permission', '=', true)->orWhere(function ($query) { $query->where('has_permission_own', '=', true) - ->where('created_by', '=', $this->currentUser->id); + ->where('created_by', '=', $this->currentUser()->id); }); }); }); }); - + return $q; } /** @@ -555,11 +576,15 @@ class PermissionService */ public function filterRelatedPages($query, $tableName, $entityIdColumn) { - if ($this->isAdmin) return $query; + if ($this->isAdmin()) { + $this->clean(); + return $query; + } + $this->currentAction = 'view'; $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn]; - return $query->where(function ($query) use ($tableDetails) { + $q = $query->where(function ($query) use ($tableDetails) { $query->where(function ($query) use (&$tableDetails) { $query->whereExists(function ($permissionQuery) use (&$tableDetails) { $permissionQuery->select('id')->from('joint_permissions') @@ -570,12 +595,50 @@ class PermissionService ->where(function ($query) { $query->where('has_permission', '=', true)->orWhere(function ($query) { $query->where('has_permission_own', '=', true) - ->where('created_by', '=', $this->currentUser->id); + ->where('created_by', '=', $this->currentUser()->id); }); }); }); })->orWhere($tableDetails['entityIdColumn'], '=', 0); }); + $this->clean(); + return $q; + } + + /** + * Check if the current user is an admin. + * @return bool + */ + private function isAdmin() + { + if ($this->isAdminUser === null) { + $this->isAdminUser = ($this->currentUser()->id !== null) ? $this->currentUser()->hasSystemRole('admin') : false; + } + + return $this->isAdminUser; + } + + /** + * Get the current user + * @return User + */ + private function currentUser() + { + if ($this->currentUserModel === false) { + $this->currentUserModel = user(); + } + + return $this->currentUserModel; + } + + /** + * Clean the cached user elements. + */ + private function clean() + { + $this->currentUserModel = false; + $this->userRoles = false; + $this->isAdminUser = null; } } \ No newline at end of file