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;
/**
* 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
}
/**
}
- foreach ($this->currentUser->roles as $role) {
+ foreach ($this->currentUser()->roles as $role) {
$roles[] = $role->id;
}
return $roles;
$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) {
*/
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));
}
$this->currentAction = $permission;
}
-
- return $this->entityRestrictionQuery($baseQuery)->count() > 0;
+ $q = $this->entityRestrictionQuery($baseQuery)->count() > 0;
+ $this->clean();
+ return $q;
}
/**
*/
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)
$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);
}
*/
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'])
->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;
}
/**
*/
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')
->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