mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Rolled out new permissions system throughout application
This commit is contained in:
parent
ada7c83e96
commit
a81a56706e
@ -70,7 +70,20 @@ abstract class Entity extends Ownable
|
|||||||
*/
|
*/
|
||||||
public function hasRestriction($role_id, $action)
|
public function hasRestriction($role_id, $action)
|
||||||
{
|
{
|
||||||
return $this->restrictions->where('role_id', $role_id)->where('action', $action)->count() > 0;
|
return $this->restrictions()->where('role_id', '=', $role_id)
|
||||||
|
->where('action', '=', $action)->count() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this entity has live (active) restrictions in place.
|
||||||
|
* @param $role_id
|
||||||
|
* @param $action
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasActiveRestriction($role_id, $action)
|
||||||
|
{
|
||||||
|
return $this->restricted && $this->restrictions()
|
||||||
|
->where('role_id', '=', $role_id)->where('action', '=', $action)->count() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,11 +71,7 @@ class BookController extends Controller
|
|||||||
'name' => 'required|string|max:255',
|
'name' => 'required|string|max:255',
|
||||||
'description' => 'string|max:1000'
|
'description' => 'string|max:1000'
|
||||||
]);
|
]);
|
||||||
$book = $this->bookRepo->newFromInput($request->all());
|
$book = $this->bookRepo->createFromInput($request->all());
|
||||||
$book->slug = $this->bookRepo->findSuitableSlug($book->name);
|
|
||||||
$book->created_by = Auth::user()->id;
|
|
||||||
$book->updated_by = Auth::user()->id;
|
|
||||||
$book->save();
|
|
||||||
Activity::add($book, 'book_create', $book->id);
|
Activity::add($book, 'book_create', $book->id);
|
||||||
return redirect($book->getUrl());
|
return redirect($book->getUrl());
|
||||||
}
|
}
|
||||||
@ -122,10 +118,7 @@ class BookController extends Controller
|
|||||||
'name' => 'required|string|max:255',
|
'name' => 'required|string|max:255',
|
||||||
'description' => 'string|max:1000'
|
'description' => 'string|max:1000'
|
||||||
]);
|
]);
|
||||||
$book->fill($request->all());
|
$book = $this->bookRepo->updateFromInput($book, $request->all());
|
||||||
$book->slug = $this->bookRepo->findSuitableSlug($book->name, $book->id);
|
|
||||||
$book->updated_by = Auth::user()->id;
|
|
||||||
$book->save();
|
|
||||||
Activity::add($book, 'book_update', $book->id);
|
Activity::add($book, 'book_update', $book->id);
|
||||||
return redirect($book->getUrl());
|
return redirect($book->getUrl());
|
||||||
}
|
}
|
||||||
@ -210,6 +203,7 @@ class BookController extends Controller
|
|||||||
// Add activity for books
|
// Add activity for books
|
||||||
foreach ($sortedBooks as $bookId) {
|
foreach ($sortedBooks as $bookId) {
|
||||||
$updatedBook = $this->bookRepo->getById($bookId);
|
$updatedBook = $this->bookRepo->getById($bookId);
|
||||||
|
$this->bookRepo->updateBookPermissions($updatedBook);
|
||||||
Activity::add($updatedBook, 'book_sort', $updatedBook->id);
|
Activity::add($updatedBook, 'book_sort', $updatedBook->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +221,7 @@ class BookController extends Controller
|
|||||||
$this->checkOwnablePermission('book-delete', $book);
|
$this->checkOwnablePermission('book-delete', $book);
|
||||||
Activity::addMessage('book_delete', 0, $book->name);
|
Activity::addMessage('book_delete', 0, $book->name);
|
||||||
Activity::removeEntity($book);
|
Activity::removeEntity($book);
|
||||||
$this->bookRepo->destroyBySlug($bookSlug);
|
$this->bookRepo->destroy($book);
|
||||||
return redirect('/books');
|
return redirect('/books');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,12 +57,9 @@ class ChapterController extends Controller
|
|||||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||||
$this->checkOwnablePermission('chapter-create', $book);
|
$this->checkOwnablePermission('chapter-create', $book);
|
||||||
|
|
||||||
$chapter = $this->chapterRepo->newFromInput($request->all());
|
$input = $request->all();
|
||||||
$chapter->slug = $this->chapterRepo->findSuitableSlug($chapter->name, $book->id);
|
$input['priority'] = $this->bookRepo->getNewPriority($book);
|
||||||
$chapter->priority = $this->bookRepo->getNewPriority($book);
|
$chapter = $this->chapterRepo->createFromInput($request->all(), $book);
|
||||||
$chapter->created_by = auth()->user()->id;
|
|
||||||
$chapter->updated_by = auth()->user()->id;
|
|
||||||
$book->chapters()->save($chapter);
|
|
||||||
Activity::add($chapter, 'chapter_create', $book->id);
|
Activity::add($chapter, 'chapter_create', $book->id);
|
||||||
return redirect($chapter->getUrl());
|
return redirect($chapter->getUrl());
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use BookStack\Exceptions\PermissionsException;
|
use BookStack\Exceptions\PermissionsException;
|
||||||
use BookStack\Repos\PermissionsRepo;
|
use BookStack\Repos\PermissionsRepo;
|
||||||
|
use BookStack\Services\RestrictionService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use BookStack\Http\Requests;
|
use BookStack\Http\Requests;
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
<?php
|
<?php namespace BookStack;
|
||||||
|
|
||||||
namespace BookStack;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
@ -16,7 +14,7 @@ class Permission extends Model
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the permission object by name.
|
* Get the permission object by name.
|
||||||
* @param $roleName
|
* @param $name
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public static function getByName($name)
|
public static function getByName($name)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace BookStack\Repos;
|
<?php namespace BookStack\Repos;
|
||||||
|
|
||||||
|
use Alpha\B;
|
||||||
use BookStack\Exceptions\NotFoundException;
|
use BookStack\Exceptions\NotFoundException;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use BookStack\Book;
|
use BookStack\Book;
|
||||||
@ -123,21 +124,43 @@ class BookRepo extends EntityRepo
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a new book instance from request input.
|
* Get a new book instance from request input.
|
||||||
* @param $input
|
* @param array $input
|
||||||
* @return Book
|
* @return Book
|
||||||
*/
|
*/
|
||||||
public function newFromInput($input)
|
public function createFromInput($input)
|
||||||
{
|
{
|
||||||
return $this->book->newInstance($input);
|
$book = $this->book->newInstance($input);
|
||||||
|
$book->slug = $this->findSuitableSlug($book->name);
|
||||||
|
$book->created_by = auth()->user()->id;
|
||||||
|
$book->updated_by = auth()->user()->id;
|
||||||
|
$book->save();
|
||||||
|
$this->restrictionService->buildEntityPermissionsForEntity($book);
|
||||||
|
return $book;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy a book identified by the given slug.
|
* Update the given book from user input.
|
||||||
* @param $bookSlug
|
* @param Book $book
|
||||||
|
* @param $input
|
||||||
|
* @return Book
|
||||||
*/
|
*/
|
||||||
public function destroyBySlug($bookSlug)
|
public function updateFromInput(Book $book, $input)
|
||||||
|
{
|
||||||
|
$book->fill($input);
|
||||||
|
$book->slug = $this->findSuitableSlug($book->name, $book->id);
|
||||||
|
$book->updated_by = auth()->user()->id;
|
||||||
|
$book->save();
|
||||||
|
$this->restrictionService->buildEntityPermissionsForEntity($book);
|
||||||
|
return $book;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the given book.
|
||||||
|
* @param Book $book
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function destroy(Book $book)
|
||||||
{
|
{
|
||||||
$book = $this->getBySlug($bookSlug);
|
|
||||||
foreach ($book->pages as $page) {
|
foreach ($book->pages as $page) {
|
||||||
$this->pageRepo->destroy($page);
|
$this->pageRepo->destroy($page);
|
||||||
}
|
}
|
||||||
@ -146,9 +169,19 @@ class BookRepo extends EntityRepo
|
|||||||
}
|
}
|
||||||
$book->views()->delete();
|
$book->views()->delete();
|
||||||
$book->restrictions()->delete();
|
$book->restrictions()->delete();
|
||||||
|
$this->restrictionService->deleteEntityPermissionsForEntity($book);
|
||||||
$book->delete();
|
$book->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias method to update the book permissions in the RestrictionService.
|
||||||
|
* @param Book $book
|
||||||
|
*/
|
||||||
|
public function updateBookPermissions(Book $book)
|
||||||
|
{
|
||||||
|
$this->restrictionService->buildEntityPermissionsForEntity($book);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next child element priority.
|
* Get the next child element priority.
|
||||||
* @param Book $book
|
* @param Book $book
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
use Activity;
|
use Activity;
|
||||||
|
use BookStack\Book;
|
||||||
use BookStack\Exceptions\NotFoundException;
|
use BookStack\Exceptions\NotFoundException;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use BookStack\Chapter;
|
use BookStack\Chapter;
|
||||||
@ -78,11 +79,18 @@ class ChapterRepo extends EntityRepo
|
|||||||
/**
|
/**
|
||||||
* Create a new chapter from request input.
|
* Create a new chapter from request input.
|
||||||
* @param $input
|
* @param $input
|
||||||
* @return $this
|
* @param Book $book
|
||||||
|
* @return Chapter
|
||||||
*/
|
*/
|
||||||
public function newFromInput($input)
|
public function createFromInput($input, Book $book)
|
||||||
{
|
{
|
||||||
return $this->chapter->fill($input);
|
$chapter = $this->chapter->newInstance($input);
|
||||||
|
$chapter->slug = $this->findSuitableSlug($chapter->name, $book->id);
|
||||||
|
$chapter->created_by = auth()->user()->id;
|
||||||
|
$chapter->updated_by = auth()->user()->id;
|
||||||
|
$chapter = $book->chapters()->save($chapter);
|
||||||
|
$this->restrictionService->buildEntityPermissionsForEntity($chapter);
|
||||||
|
return $chapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,6 +108,7 @@ class ChapterRepo extends EntityRepo
|
|||||||
Activity::removeEntity($chapter);
|
Activity::removeEntity($chapter);
|
||||||
$chapter->views()->delete();
|
$chapter->views()->delete();
|
||||||
$chapter->restrictions()->delete();
|
$chapter->restrictions()->delete();
|
||||||
|
$this->restrictionService->deleteEntityPermissionsForEntity($chapter);
|
||||||
$chapter->delete();
|
$chapter->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +151,7 @@ class EntityRepo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$entity->save();
|
$entity->save();
|
||||||
|
$this->restrictionService->buildEntityPermissionsForEntity($entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -168,6 +168,7 @@ class PageRepo extends EntityRepo
|
|||||||
if ($chapter) $page->chapter_id = $chapter->id;
|
if ($chapter) $page->chapter_id = $chapter->id;
|
||||||
|
|
||||||
$book->pages()->save($page);
|
$book->pages()->save($page);
|
||||||
|
$this->restrictionService->buildEntityPermissionsForEntity($page);
|
||||||
return $page;
|
return $page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,6 +584,7 @@ class PageRepo extends EntityRepo
|
|||||||
$page->views()->delete();
|
$page->views()->delete();
|
||||||
$page->revisions()->delete();
|
$page->revisions()->delete();
|
||||||
$page->restrictions()->delete();
|
$page->restrictions()->delete();
|
||||||
|
$this->restrictionService->deleteEntityPermissionsForEntity($page);
|
||||||
$page->delete();
|
$page->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
use BookStack\Exceptions\PermissionsException;
|
use BookStack\Exceptions\PermissionsException;
|
||||||
use BookStack\Permission;
|
use BookStack\Permission;
|
||||||
use BookStack\Role;
|
use BookStack\Role;
|
||||||
|
use BookStack\Services\RestrictionService;
|
||||||
use Setting;
|
use Setting;
|
||||||
|
|
||||||
class PermissionsRepo
|
class PermissionsRepo
|
||||||
@ -11,16 +12,19 @@ class PermissionsRepo
|
|||||||
|
|
||||||
protected $permission;
|
protected $permission;
|
||||||
protected $role;
|
protected $role;
|
||||||
|
protected $restrictionService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PermissionsRepo constructor.
|
* PermissionsRepo constructor.
|
||||||
* @param $permission
|
* @param Permission $permission
|
||||||
* @param $role
|
* @param Role $role
|
||||||
|
* @param RestrictionService $restrictionService
|
||||||
*/
|
*/
|
||||||
public function __construct(Permission $permission, Role $role)
|
public function __construct(Permission $permission, Role $role, RestrictionService $restrictionService)
|
||||||
{
|
{
|
||||||
$this->permission = $permission;
|
$this->permission = $permission;
|
||||||
$this->role = $role;
|
$this->role = $role;
|
||||||
|
$this->restrictionService = $restrictionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,6 +73,7 @@ class PermissionsRepo
|
|||||||
|
|
||||||
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
|
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
|
||||||
$this->assignRolePermissions($role, $permissions);
|
$this->assignRolePermissions($role, $permissions);
|
||||||
|
$this->restrictionService->buildEntityPermissionForRole($role);
|
||||||
return $role;
|
return $role;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +96,7 @@ class PermissionsRepo
|
|||||||
|
|
||||||
$role->fill($roleData);
|
$role->fill($roleData);
|
||||||
$role->save();
|
$role->save();
|
||||||
|
$this->restrictionService->buildEntityPermissionForRole($role);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,6 +142,7 @@ class PermissionsRepo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->restrictionService->deleteEntityPermissionsForRole($role);
|
||||||
$role->delete();
|
$role->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,15 @@ class Role extends Model
|
|||||||
return $this->belongsToMany('BookStack\User');
|
return $this->belongsToMany('BookStack\User');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all related entity permissions.
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
*/
|
||||||
|
public function entityPermissions()
|
||||||
|
{
|
||||||
|
return $this->hasMany(EntityPermission::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The permissions that belong to the role.
|
* The permissions that belong to the role.
|
||||||
*/
|
*/
|
||||||
|
@ -105,8 +105,16 @@ class ActivityService
|
|||||||
*/
|
*/
|
||||||
public function entityActivity($entity, $count = 20, $page = 0)
|
public function entityActivity($entity, $count = 20, $page = 0)
|
||||||
{
|
{
|
||||||
$activity = $entity->hasMany('BookStack\Activity')->orderBy('created_at', 'desc')
|
if ($entity->isA('book')) {
|
||||||
->skip($count * $page)->take($count)->get();
|
$query = $this->activity->where('book_id', '=', $entity->id);
|
||||||
|
} else {
|
||||||
|
$query = $this->activity->where('entity_type', '=', get_class($entity))
|
||||||
|
->where('entity_id', '=', $entity->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$activity = $this->restrictionService
|
||||||
|
->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type')
|
||||||
|
->orderBy('created_at', 'desc')->skip($count * $page)->take($count)->get();
|
||||||
|
|
||||||
return $this->filterSimilar($activity);
|
return $this->filterSimilar($activity);
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,14 @@ class RestrictionService
|
|||||||
protected $entityPermission;
|
protected $entityPermission;
|
||||||
protected $role;
|
protected $role;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actions that have permissions attached throughout the application.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
protected $actions = ['view', 'create', 'update', 'delete'];
|
protected $actions = ['view', 'create', 'update', 'delete'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RestrictionService constructor.
|
* RestrictionService constructor.
|
||||||
* TODO - Handle events when roles or entities change.
|
|
||||||
* @param EntityPermission $entityPermission
|
* @param EntityPermission $entityPermission
|
||||||
* @param Book $book
|
* @param Book $book
|
||||||
* @param Chapter $chapter
|
* @param Chapter $chapter
|
||||||
@ -73,6 +76,92 @@ class RestrictionService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the entity permissions for a particular entity.
|
||||||
|
* @param Entity $entity
|
||||||
|
*/
|
||||||
|
public function buildEntityPermissionsForEntity(Entity $entity)
|
||||||
|
{
|
||||||
|
$roles = $this->role->load('permissions')->all();
|
||||||
|
$entities = collect([$entity]);
|
||||||
|
|
||||||
|
if ($entity->isA('book')) {
|
||||||
|
$entities = $entities->merge($entity->chapters);
|
||||||
|
$entities = $entities->merge($entity->pages);
|
||||||
|
} elseif ($entity->isA('chapter')) {
|
||||||
|
$entities = $entities->merge($entity->pages);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->deleteManyEntityPermissionsForEntities($entities);
|
||||||
|
$this->createManyEntityPermissions($entities, $roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the entity permissions for a particular role.
|
||||||
|
* @param Role $role
|
||||||
|
*/
|
||||||
|
public function buildEntityPermissionForRole(Role $role)
|
||||||
|
{
|
||||||
|
$roles = collect([$role]);
|
||||||
|
|
||||||
|
$this->deleteManyEntityPermissionsForRoles($roles);
|
||||||
|
|
||||||
|
// Chunk through all books
|
||||||
|
$this->book->chunk(500, function ($books) use ($roles) {
|
||||||
|
$this->createManyEntityPermissions($books, $roles);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chunk through all chapters
|
||||||
|
$this->chapter->with('book')->chunk(500, function ($books) use ($roles) {
|
||||||
|
$this->createManyEntityPermissions($books, $roles);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chunk through all pages
|
||||||
|
$this->page->with('book', 'chapter')->chunk(500, function ($books) use ($roles) {
|
||||||
|
$this->createManyEntityPermissions($books, $roles);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the entity permissions attached to a particular role.
|
||||||
|
* @param Role $role
|
||||||
|
*/
|
||||||
|
public function deleteEntityPermissionsForRole(Role $role)
|
||||||
|
{
|
||||||
|
$this->deleteManyEntityPermissionsForRoles([$role]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all of the entity permissions for a list of entities.
|
||||||
|
* @param Role[] $roles
|
||||||
|
*/
|
||||||
|
protected function deleteManyEntityPermissionsForRoles($roles)
|
||||||
|
{
|
||||||
|
foreach ($roles as $role) {
|
||||||
|
$role->entityPermissions()->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the entity permissions for a particular entity.
|
||||||
|
* @param Entity $entity
|
||||||
|
*/
|
||||||
|
public function deleteEntityPermissionsForEntity(Entity $entity)
|
||||||
|
{
|
||||||
|
$this->deleteManyEntityPermissionsForEntities([$entity]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all of the entity permissions for a list of entities.
|
||||||
|
* @param Entity[] $entities
|
||||||
|
*/
|
||||||
|
protected function deleteManyEntityPermissionsForEntities($entities)
|
||||||
|
{
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
$entity->permissions()->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create & Save entity permissions for many entities and permissions.
|
* Create & Save entity permissions for many entities and permissions.
|
||||||
* @param Collection $entities
|
* @param Collection $entities
|
||||||
@ -88,10 +177,18 @@ class RestrictionService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
\Log::info(collect($entityPermissions)->where('entity_id', 1)->where('entity_type', 'BookStack\\Page')->where('role_id', 2)->all());
|
||||||
$this->entityPermission->insert($entityPermissions);
|
$this->entityPermission->insert($entityPermissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create entity permission data for an entity and role
|
||||||
|
* for a particular action.
|
||||||
|
* @param Entity $entity
|
||||||
|
* @param Role $role
|
||||||
|
* @param $action
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
protected function createEntityPermissionData(Entity $entity, Role $role, $action)
|
protected function createEntityPermissionData(Entity $entity, Role $role, $action)
|
||||||
{
|
{
|
||||||
$permissionPrefix = $entity->getType() . '-' . $action;
|
$permissionPrefix = $entity->getType() . '-' . $action;
|
||||||
@ -103,29 +200,39 @@ class RestrictionService
|
|||||||
if (!$entity->restricted) {
|
if (!$entity->restricted) {
|
||||||
return $this->createEntityPermissionDataArray($entity, $role, $action, $roleHasPermission, $roleHasPermissionOwn);
|
return $this->createEntityPermissionDataArray($entity, $role, $action, $roleHasPermission, $roleHasPermissionOwn);
|
||||||
} else {
|
} else {
|
||||||
$hasAccess = $entity->hasRestriction($role->id, $action);
|
$hasAccess = $entity->hasActiveRestriction($role->id, $action);
|
||||||
return $this->createEntityPermissionDataArray($entity, $role, $action, $hasAccess, $hasAccess);
|
return $this->createEntityPermissionDataArray($entity, $role, $action, $hasAccess, $hasAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
} elseif ($entity->isA('chapter')) {
|
} elseif ($entity->isA('chapter')) {
|
||||||
|
|
||||||
if (!$entity->restricted) {
|
if (!$entity->restricted) {
|
||||||
$hasAccessToBook = $entity->book->hasRestriction($role->id, $action);
|
$hasExplicitAccessToBook = $entity->book->hasActiveRestriction($role->id, $action);
|
||||||
|
$hasPermissiveAccessToBook = !$entity->book->restricted;
|
||||||
return $this->createEntityPermissionDataArray($entity, $role, $action,
|
return $this->createEntityPermissionDataArray($entity, $role, $action,
|
||||||
($roleHasPermission && $hasAccessToBook), ($roleHasPermissionOwn && $hasAccessToBook));
|
($hasExplicitAccessToBook || ($roleHasPermission && $hasPermissiveAccessToBook)),
|
||||||
|
($hasExplicitAccessToBook || ($roleHasPermissionOwn && $hasPermissiveAccessToBook)));
|
||||||
} else {
|
} else {
|
||||||
$hasAccess = $entity->hasRestriction($role->id, $action);
|
$hasAccess = $entity->hasActiveRestriction($role->id, $action);
|
||||||
return $this->createEntityPermissionDataArray($entity, $role, $action, $hasAccess, $hasAccess);
|
return $this->createEntityPermissionDataArray($entity, $role, $action, $hasAccess, $hasAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
} elseif ($entity->isA('page')) {
|
} elseif ($entity->isA('page')) {
|
||||||
|
|
||||||
if (!$entity->restricted) {
|
if (!$entity->restricted) {
|
||||||
$hasAccessToBook = $entity->book->hasRestriction($role->id, $action);
|
$hasExplicitAccessToBook = $entity->book->hasActiveRestriction($role->id, $action);
|
||||||
$hasAccessToChapter = $entity->chapter ? ($entity->chapter->hasRestriction($role->id, $action)) : true;
|
$hasPermissiveAccessToBook = !$entity->book->restricted;
|
||||||
|
$hasExplicitAccessToChapter = $entity->chapter && $entity->chapter->hasActiveRestriction($role->id, $action);
|
||||||
|
$hasPermissiveAccessToChapter = $entity->chapter && !$entity->chapter->restricted;
|
||||||
|
$acknowledgeChapter = ($entity->chapter && $entity->chapter->restricted);
|
||||||
|
|
||||||
|
$hasExplicitAccessToParents = $acknowledgeChapter ? $hasExplicitAccessToChapter : $hasExplicitAccessToBook;
|
||||||
|
$hasPermissiveAccessToParents = $acknowledgeChapter ? $hasPermissiveAccessToChapter : $hasPermissiveAccessToBook;
|
||||||
|
|
||||||
return $this->createEntityPermissionDataArray($entity, $role, $action,
|
return $this->createEntityPermissionDataArray($entity, $role, $action,
|
||||||
($roleHasPermission && $hasAccessToBook && $hasAccessToChapter),
|
($hasExplicitAccessToParents || ($roleHasPermission && $hasPermissiveAccessToParents)),
|
||||||
($roleHasPermissionOwn && $hasAccessToBook && $hasAccessToChapter));
|
($hasExplicitAccessToParents || ($roleHasPermissionOwn && $hasPermissiveAccessToParents))
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$hasAccess = $entity->hasRestriction($role->id, $action);
|
$hasAccess = $entity->hasRestriction($role->id, $action);
|
||||||
return $this->createEntityPermissionDataArray($entity, $role, $action, $hasAccess, $hasAccess);
|
return $this->createEntityPermissionDataArray($entity, $role, $action, $hasAccess, $hasAccess);
|
||||||
@ -134,6 +241,16 @@ class RestrictionService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an array of data with the information of an entity permissions.
|
||||||
|
* Used to build data for bulk insertion.
|
||||||
|
* @param Entity $entity
|
||||||
|
* @param Role $role
|
||||||
|
* @param $action
|
||||||
|
* @param $permissionAll
|
||||||
|
* @param $permissionOwn
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
protected function createEntityPermissionDataArray(Entity $entity, Role $role, $action, $permissionAll, $permissionOwn)
|
protected function createEntityPermissionDataArray(Entity $entity, Role $role, $action, $permissionAll, $permissionOwn)
|
||||||
{
|
{
|
||||||
$entityClass = get_class($entity);
|
$entityClass = get_class($entity);
|
||||||
@ -151,22 +268,30 @@ class RestrictionService
|
|||||||
/**
|
/**
|
||||||
* Checks if an entity has a restriction set upon it.
|
* Checks if an entity has a restriction set upon it.
|
||||||
* @param Entity $entity
|
* @param Entity $entity
|
||||||
* @param $action
|
* @param $permission
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function checkIfEntityRestricted(Entity $entity, $action)
|
public function checkEntityUserAccess(Entity $entity, $permission)
|
||||||
{
|
{
|
||||||
if ($this->isAdmin) return true;
|
if ($this->isAdmin) return true;
|
||||||
$this->currentAction = $action;
|
$explodedPermission = explode('-', $permission);
|
||||||
|
|
||||||
$baseQuery = $entity->where('id', '=', $entity->id);
|
$baseQuery = $entity->where('id', '=', $entity->id);
|
||||||
if ($entity->isA('page')) {
|
|
||||||
return $this->pageRestrictionQuery($baseQuery)->count() > 0;
|
$nonEntityPermissions = ['restrictions'];
|
||||||
} elseif ($entity->isA('chapter')) {
|
|
||||||
return $this->chapterRestrictionQuery($baseQuery)->count() > 0;
|
// Handle non entity specific permissions
|
||||||
} elseif ($entity->isA('book')) {
|
if (in_array($explodedPermission[0], $nonEntityPermissions)) {
|
||||||
return $this->bookRestrictionQuery($baseQuery)->count() > 0;
|
$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 === $entity->created_by;
|
||||||
|
return ($allPermission || ($isOwner && $ownPermission));
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
$action = end($explodedPermission);
|
||||||
|
$this->currentAction = $action;
|
||||||
|
return $this->entityRestrictionQuery($baseQuery)->count() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -188,29 +313,6 @@ class RestrictionService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add restrictions for a page query
|
|
||||||
* @param $query
|
|
||||||
* @param string $action
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if ($this->isAdmin) return $query;
|
|
||||||
$this->currentAction = $action;
|
|
||||||
return $this->entityRestrictionQuery($query);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The general query filter to remove all entities
|
* The general query filter to remove all entities
|
||||||
* that the current user does not have access to.
|
* that the current user does not have access to.
|
||||||
@ -234,6 +336,29 @@ class RestrictionService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add restrictions for a page query
|
||||||
|
* @param $query
|
||||||
|
* @param string $action
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($this->isAdmin) return $query;
|
||||||
|
$this->currentAction = $action;
|
||||||
|
return $this->entityRestrictionQuery($query);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add on permission restrictions to a chapter query.
|
* Add on permission restrictions to a chapter query.
|
||||||
* @param $query
|
* @param $query
|
||||||
@ -316,7 +441,7 @@ class RestrictionService
|
|||||||
->where(function ($query) {
|
->where(function ($query) {
|
||||||
$query->where('has_permission', '=', true)->orWhere(function ($query) {
|
$query->where('has_permission', '=', true)->orWhere(function ($query) {
|
||||||
$query->where('has_permission_own', '=', true)
|
$query->where('has_permission_own', '=', true)
|
||||||
->where('created_by', '=', $this->currentUser->id);
|
->where('created_by', '=', $this->currentUser ? $this->currentUser->id : 0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -45,20 +45,8 @@ function userCan($permission, \BookStack\Ownable $ownable = null)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check permission on ownable item
|
// Check permission on ownable item
|
||||||
$permissionBaseName = strtolower($permission) . '-';
|
|
||||||
$hasPermission = false;
|
|
||||||
if (auth()->user()->can($permissionBaseName . 'all')) $hasPermission = true;
|
|
||||||
if (auth()->user()->can($permissionBaseName . 'own') && $ownable->createdBy && $ownable->createdBy->id === auth()->user()->id) $hasPermission = true;
|
|
||||||
|
|
||||||
if (!$ownable instanceof \BookStack\Entity) return $hasPermission;
|
|
||||||
|
|
||||||
// Check restrictions on the entity
|
|
||||||
$restrictionService = app('BookStack\Services\RestrictionService');
|
$restrictionService = app('BookStack\Services\RestrictionService');
|
||||||
$explodedPermission = explode('-', $permission);
|
return $restrictionService->checkEntityUserAccess($ownable, $permission);
|
||||||
$action = end($explodedPermission);
|
|
||||||
$hasAccess = $restrictionService->checkIfEntityRestricted($ownable, $action);
|
|
||||||
$restrictionsSet = $restrictionService->checkIfRestrictionsSet($ownable, $action);
|
|
||||||
return ($hasAccess && $restrictionsSet) || (!$restrictionsSet && $hasPermission);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,6 +23,7 @@ class AddViewPermissionsToRoles extends Migration
|
|||||||
$newPermission->name = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op));
|
$newPermission->name = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op));
|
||||||
$newPermission->display_name = $op . ' ' . $entity . 's';
|
$newPermission->display_name = $op . ' ' . $entity . 's';
|
||||||
$newPermission->save();
|
$newPermission->save();
|
||||||
|
// Assign view permissions to all current roles
|
||||||
foreach ($currentRoles as $role) {
|
foreach ($currentRoles as $role) {
|
||||||
$role->attachPermission($newPermission);
|
$role->attachPermission($newPermission);
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,14 @@ class RestrictionsTest extends TestCase
|
|||||||
{
|
{
|
||||||
protected $user;
|
protected $user;
|
||||||
protected $viewer;
|
protected $viewer;
|
||||||
|
protected $restrictionService;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
$this->user = $this->getNewUser();
|
$this->user = $this->getNewUser();
|
||||||
$this->viewer = $this->getViewer();
|
$this->viewer = $this->getViewer();
|
||||||
|
$this->restrictionService = $this->app[\BookStack\Services\RestrictionService::class];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getViewer()
|
protected function getViewer()
|
||||||
@ -43,6 +45,8 @@ class RestrictionsTest extends TestCase
|
|||||||
}
|
}
|
||||||
$entity->save();
|
$entity->save();
|
||||||
$entity->load('restrictions');
|
$entity->load('restrictions');
|
||||||
|
$this->restrictionService->buildEntityPermissionsForEntity($entity);
|
||||||
|
$entity->load('permissions');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_book_view_restriction()
|
public function test_book_view_restriction()
|
||||||
|
@ -7,7 +7,15 @@ class RolesTest extends TestCase
|
|||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
$this->user = $this->getNewBlankUser();
|
$this->user = $this->getViewer();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getViewer()
|
||||||
|
{
|
||||||
|
$role = \BookStack\Role::getRole('viewer');
|
||||||
|
$viewer = $this->getNewBlankUser();
|
||||||
|
$viewer->attachRole($role);;
|
||||||
|
return $viewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,7 +149,7 @@ class RolesTest extends TestCase
|
|||||||
|
|
||||||
public function test_restrictions_manage_own_permission()
|
public function test_restrictions_manage_own_permission()
|
||||||
{
|
{
|
||||||
$otherUsersPage = \BookStack\Page::take(1)->get()->first();
|
$otherUsersPage = \BookStack\Page::first();
|
||||||
$content = $this->createEntityChainBelongingToUser($this->user);
|
$content = $this->createEntityChainBelongingToUser($this->user);
|
||||||
// Check can't restrict other's content
|
// Check can't restrict other's content
|
||||||
$this->actingAs($this->user)->visit($otherUsersPage->getUrl())
|
$this->actingAs($this->user)->visit($otherUsersPage->getUrl())
|
||||||
|
@ -65,6 +65,8 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
|
|||||||
$page = factory(BookStack\Page::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id]);
|
$page = factory(BookStack\Page::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id]);
|
||||||
$book->chapters()->saveMany([$chapter]);
|
$book->chapters()->saveMany([$chapter]);
|
||||||
$chapter->pages()->saveMany([$page]);
|
$chapter->pages()->saveMany([$page]);
|
||||||
|
$restrictionService = $this->app[\BookStack\Services\RestrictionService::class];
|
||||||
|
$restrictionService->buildEntityPermissionsForEntity($book);
|
||||||
return [
|
return [
|
||||||
'book' => $book,
|
'book' => $book,
|
||||||
'chapter' => $chapter,
|
'chapter' => $chapter,
|
||||||
|
Loading…
Reference in New Issue
Block a user