namespace BookStack\Actions;
-use BookStack\Auth\Permissions\PermissionService;
use BookStack\Entities\Models\Entity;
use BookStack\Interfaces\Loggable;
use Illuminate\Database\Eloquent\Builder;
class ActivityLogger
{
- protected $permissionService;
-
- public function __construct(PermissionService $permissionService)
- {
- $this->permissionService = $permissionService;
- }
-
/**
* Add a generic activity event to the database.
*
namespace BookStack\Actions;
-use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Auth\User;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
class ActivityQueries
{
- protected $permissionService;
+ protected PermissionApplicator $permissions;
- public function __construct(PermissionService $permissionService)
+ public function __construct(PermissionApplicator $permissions)
{
- $this->permissionService = $permissionService;
+ $this->permissions = $permissions;
}
/**
*/
public function latest(int $count = 20, int $page = 0): array
{
- $activityList = $this->permissionService
+ $activityList = $this->permissions
->filterRestrictedEntityRelations(Activity::query(), 'activities', 'entity_id', 'entity_type')
->orderBy('created_at', 'desc')
->with(['user', 'entity'])
*/
public function userActivity(User $user, int $count = 20, int $page = 0): array
{
- $activityList = $this->permissionService
+ $activityList = $this->permissions
->filterRestrictedEntityRelations(Activity::query(), 'activities', 'entity_id', 'entity_type')
->orderBy('created_at', 'desc')
->where('user_id', '=', $user->id)
namespace BookStack\Actions;
-use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Entities\Models\Entity;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
class TagRepo
{
- protected $tag;
- protected $permissionService;
+ protected PermissionApplicator $permissions;
- public function __construct(PermissionService $ps)
+ public function __construct(PermissionApplicator $permissions)
{
- $this->permissionService = $ps;
+ $this->permissions = $permissions;
}
/**
});
}
- return $this->permissionService->filterRestrictedEntityRelations($query, 'tags', 'entity_id', 'entity_type');
+ return $this->permissions->filterRestrictedEntityRelations($query, 'tags', 'entity_id', 'entity_type');
}
/**
$query = $query->orderBy('count', 'desc')->take(50);
}
- $query = $this->permissionService->filterRestrictedEntityRelations($query, 'tags', 'entity_id', 'entity_type');
+ $query = $this->permissions->filterRestrictedEntityRelations($query, 'tags', 'entity_id', 'entity_type');
return $query->get(['name'])->pluck('name');
}
$query = $query->where('name', '=', $tagName);
}
- $query = $this->permissionService->filterRestrictedEntityRelations($query, 'tags', 'entity_id', 'entity_type');
+ $query = $this->permissions->filterRestrictedEntityRelations($query, 'tags', 'entity_id', 'entity_type');
return $query->get(['value'])->pluck('value');
}
*/
protected $entityCache;
+ /**
+ * Re-generate all entity permission from scratch.
+ */
+ public function rebuildForAll()
+ {
+ JointPermission::query()->truncate();
+ $this->readyEntityCache();
+
+ // Get all roles (Should be the most limited dimension)
+ $roles = Role::query()->with('permissions')->get()->all();
+
+ // Chunk through all books
+ $this->bookFetchQuery()->chunk(5, function (EloquentCollection $books) use ($roles) {
+ $this->buildJointPermissionsForBooks($books, $roles);
+ });
+
+ // Chunk through all bookshelves
+ Bookshelf::query()->withTrashed()->select(['id', 'restricted', 'owned_by'])
+ ->chunk(50, function (EloquentCollection $shelves) use ($roles) {
+ $this->createManyJointPermissions($shelves->all(), $roles);
+ });
+ }
+
+ /**
+ * Rebuild the entity jointPermissions for a particular entity.
+ *
+ * @throws Throwable
+ */
+ public function rebuildForEntity(Entity $entity)
+ {
+ $entities = [$entity];
+ if ($entity instanceof Book) {
+ $books = $this->bookFetchQuery()->where('id', '=', $entity->id)->get();
+ $this->buildJointPermissionsForBooks($books, Role::query()->with('permissions')->get()->all(), true);
+
+ return;
+ }
+
+ /** @var BookChild $entity */
+ if ($entity->book) {
+ $entities[] = $entity->book;
+ }
+
+ if ($entity instanceof Page && $entity->chapter_id) {
+ $entities[] = $entity->chapter;
+ }
+
+ if ($entity instanceof Chapter) {
+ foreach ($entity->pages as $page) {
+ $entities[] = $page;
+ }
+ }
+
+ $this->buildJointPermissionsForEntities($entities);
+ }
+
+ /**
+ * Build the entity jointPermissions for a particular role.
+ */
+ public function rebuildForRole(Role $role)
+ {
+ $roles = [$role];
+ $role->jointPermissions()->delete();
+
+ // Chunk through all books
+ $this->bookFetchQuery()->chunk(20, function ($books) use ($roles) {
+ $this->buildJointPermissionsForBooks($books, $roles);
+ });
+
+ // Chunk through all bookshelves
+ Bookshelf::query()->select(['id', 'restricted', 'owned_by'])
+ ->chunk(50, function ($shelves) use ($roles) {
+ $this->createManyJointPermissions($shelves->all(), $roles);
+ });
+ }
+
/**
* Prepare the local entity cache and ensure it's empty.
*
->find($chapterId);
}
- /**
- * Re-generate all entity permission from scratch.
- */
- public function buildJointPermissions()
- {
- JointPermission::query()->truncate();
- $this->readyEntityCache();
-
- // Get all roles (Should be the most limited dimension)
- $roles = Role::query()->with('permissions')->get()->all();
-
- // Chunk through all books
- $this->bookFetchQuery()->chunk(5, function (EloquentCollection $books) use ($roles) {
- $this->buildJointPermissionsForBooks($books, $roles);
- });
-
- // Chunk through all bookshelves
- Bookshelf::query()->withTrashed()->select(['id', 'restricted', 'owned_by'])
- ->chunk(50, function (EloquentCollection $shelves) use ($roles) {
- $this->buildJointPermissionsForShelves($shelves, $roles);
- });
- }
-
/**
* Get a query for fetching a book with it's children.
*/
]);
}
- /**
- * Build joint permissions for the given shelf and role combinations.
- *
- * @throws Throwable
- */
- protected function buildJointPermissionsForShelves(EloquentCollection $shelves, array $roles, bool $deleteOld = false)
- {
- if ($deleteOld) {
- $this->deleteManyJointPermissionsForEntities($shelves->all());
- }
- $this->createManyJointPermissions($shelves->all(), $roles);
- }
/**
* Build joint permissions for the given book and role combinations.
$this->createManyJointPermissions($entities->all(), $roles);
}
- /**
- * Rebuild the entity jointPermissions for a particular entity.
- *
- * @throws Throwable
- */
- public function buildJointPermissionsForEntity(Entity $entity)
- {
- $entities = [$entity];
- if ($entity instanceof Book) {
- $books = $this->bookFetchQuery()->where('id', '=', $entity->id)->get();
- $this->buildJointPermissionsForBooks($books, Role::query()->with('permissions')->get()->all(), true);
-
- return;
- }
-
- /** @var BookChild $entity */
- if ($entity->book) {
- $entities[] = $entity->book;
- }
-
- if ($entity instanceof Page && $entity->chapter_id) {
- $entities[] = $entity->chapter;
- }
-
- if ($entity instanceof Chapter) {
- foreach ($entity->pages as $page) {
- $entities[] = $page;
- }
- }
-
- $this->buildJointPermissionsForEntities($entities);
- }
-
/**
* Rebuild the entity jointPermissions for a collection of entities.
*
$this->createManyJointPermissions($entities, $roles);
}
- /**
- * Build the entity jointPermissions for a particular role.
- */
- public function buildJointPermissionForRole(Role $role)
- {
- $roles = [$role];
- $role->jointPermissions()->delete();
-
- // Chunk through all books
- $this->bookFetchQuery()->chunk(20, function ($books) use ($roles) {
- $this->buildJointPermissionsForBooks($books, $roles);
- });
-
- // Chunk through all bookshelves
- Bookshelf::query()->select(['id', 'restricted', 'owned_by'])
- ->chunk(50, function ($shelves) use ($roles) {
- $this->buildJointPermissionsForShelves($shelves, $roles);
- });
- }
-
/**
* Delete all the entity jointPermissions for a list of entities.
*
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Builder as QueryBuilder;
-class PermissionService
+class PermissionApplicator
{
/**
- * @var ?array
+ * @var ?array<int>
*/
protected $userRoles = null;
*/
protected $currentUserModel = null;
-
/**
* Get the roles for the current logged in user.
*/
*/
public function getAllRoles(): Collection
{
- return Role::query()->all();
+ return Role::query()->get();
}
/**
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
$this->assignRolePermissions($role, $permissions);
- $this->permissionBuilder->buildJointPermissionForRole($role);
+ $this->permissionBuilder->rebuildForRole($role);
Activity::add(ActivityType::ROLE_CREATE, $role);
$role->fill($roleData);
$role->mfa_enforced = ($roleData['mfa_enforced'] ?? 'false') === 'true';
$role->save();
- $this->permissionBuilder->buildJointPermissionForRole($role);
+ $this->permissionBuilder->rebuildForRole($role);
Activity::add(ActivityType::ROLE_UPDATE, $role);
}
$permissionNameArray = array_values($permissionNameArray);
if ($permissionNameArray) {
- $permissions = EntityPermission::query()
+ $permissions = RolePermission::query()
->whereIn('name', $permissionNameArray)
->pluck('id')
->toArray();
// Custom BookStack
'Activity' => BookStack\Facades\Activity::class,
- 'Permissions' => BookStack\Facades\Permissions::class,
'Theme' => BookStack\Facades\Theme::class,
],
{
$connection = DB::getDefaultConnection();
- if ($this->hasOption('database')) {
+ if ($this->option('database')) {
DB::setDefaultConnection($this->option('database'));
}
- $this->permissionBuilder->buildJointPermissions();
+ $this->permissionBuilder->rebuildForAll();
DB::setDefaultConnection($connection);
$this->comment('Permissions regenerated');
use BookStack\Actions\View;
use BookStack\Auth\Permissions\EntityPermission;
use BookStack\Auth\Permissions\JointPermission;
+use BookStack\Auth\Permissions\JointPermissionBuilder;
+use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Entities\Tools\SearchIndex;
use BookStack\Entities\Tools\SlugGenerator;
-use BookStack\Facades\Permissions;
use BookStack\Interfaces\Deletable;
use BookStack\Interfaces\Favouritable;
use BookStack\Interfaces\Loggable;
*/
public function scopeHasPermission(Builder $query, string $permission)
{
- return Permissions::restrictEntityQuery($query, $permission);
+ return app()->make(PermissionApplicator::class)->restrictEntityQuery($query, $permission);
}
/**
*/
public function rebuildPermissions()
{
- /** @noinspection PhpUnhandledExceptionInspection */
- Permissions::buildJointPermissionsForEntity(clone $this);
+ app()->make(JointPermissionBuilder::class)->rebuildForEntity(clone $this);
}
/**
*/
public function indexForSearch()
{
- app(SearchIndex::class)->indexEntity(clone $this);
+ app()->make(SearchIndex::class)->indexEntity(clone $this);
}
/**
*/
public function refreshSlug(): string
{
- $this->slug = app(SlugGenerator::class)->generate($this);
+ $this->slug = app()->make(SlugGenerator::class)->generate($this);
return $this->slug;
}
namespace BookStack\Entities\Models;
+use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Entities\Tools\PageContent;
-use BookStack\Facades\Permissions;
use BookStack\Uploads\Attachment;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
*/
public function scopeVisible(Builder $query): Builder
{
- $query = Permissions::enforceDraftVisibilityOnQuery($query);
+ $query = app()->make(PermissionApplicator::class)->enforceDraftVisibilityOnQuery($query);
return parent::scopeVisible($query);
}
namespace BookStack\Entities\Queries;
-use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Entities\EntityProvider;
abstract class EntityQuery
{
- protected function permissionService(): PermissionService
+ protected function permissionService(): PermissionApplicator
{
- return app()->make(PermissionService::class);
+ return app()->make(PermissionApplicator::class);
}
protected function entityProvider(): EntityProvider
namespace BookStack\Entities\Tools;
-use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Auth\User;
use BookStack\Entities\EntityProvider;
use BookStack\Entities\Models\BookChild;
class SearchRunner
{
- /**
- * @var EntityProvider
- */
- protected $entityProvider;
- /**
- * @var PermissionService
- */
- protected $permissionService;
+ protected EntityProvider $entityProvider;
+ protected PermissionApplicator $permissions;
/**
* Acceptable operators to be used in a query.
*
- * @var array
+ * @var string[]
*/
protected $queryOperators = ['<=', '>=', '=', '<', '>', 'like', '!='];
*/
protected $termAdjustmentCache;
- public function __construct(EntityProvider $entityProvider, PermissionService $permissionService)
+ public function __construct(EntityProvider $entityProvider, PermissionApplicator $permissions)
{
$this->entityProvider = $entityProvider;
- $this->permissionService = $permissionService;
+ $this->permissions = $permissions;
$this->termAdjustmentCache = new SplObjectStorage();
}
}
}
- return $this->permissionService->enforceEntityRestrictions($entityModelInstance, $entityQuery, $action);
+ return $this->permissions->enforceEntityRestrictions($entityModelInstance, $entityQuery, $action);
}
/**
+++ /dev/null
-<?php
-
-namespace BookStack\Facades;
-
-use Illuminate\Support\Facades\Facade;
-
-class Permissions extends Facade
-{
- /**
- * Get the registered name of the component.
- *
- * @return string
- */
- protected static function getFacadeAccessor()
- {
- return 'permissions';
- }
-}
namespace BookStack\Providers;
use BookStack\Actions\ActivityLogger;
-use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Theming\ThemeService;
use BookStack\Uploads\ImageService;
use Illuminate\Support\ServiceProvider;
return $this->app->make(ActivityLogger::class);
});
- $this->app->singleton('images', function () {
- return $this->app->make(ImageService::class);
- });
-
- $this->app->singleton('permissions', function () {
- return $this->app->make(PermissionService::class);
- });
-
$this->app->singleton('theme', function () {
return $this->app->make(ThemeService::class);
});
namespace BookStack\Uploads;
-use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Auth\User;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
*/
public function scopeVisible(): Builder
{
- $permissionService = app()->make(PermissionService::class);
+ $permissions = app()->make(PermissionApplicator::class);
- return $permissionService->filterRelatedEntity(
+ return $permissions->filterRelatedEntity(
Page::class,
self::query(),
'attachments',
namespace BookStack\Uploads;
-use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Entities\Models\Page;
use BookStack\Exceptions\ImageUploadException;
use Exception;
class ImageRepo
{
- protected $imageService;
- protected $restrictionService;
+ protected ImageService $imageService;
+ protected PermissionApplicator $permissions;
/**
* ImageRepo constructor.
*/
- public function __construct(ImageService $imageService, PermissionService $permissionService)
+ public function __construct(ImageService $imageService, PermissionApplicator $permissions)
{
$this->imageService = $imageService;
- $this->restrictionService = $permissionService;
+ $this->permissions = $permissions;
}
/**
}
// Filter by page access
- $imageQuery = $this->restrictionService->filterRelatedEntity(Page::class, $imageQuery, 'images', 'uploaded_to');
+ $imageQuery = $this->permissions->filterRelatedEntity(Page::class, $imageQuery, 'images', 'uploaded_to');
if ($whereClause !== null) {
$imageQuery = $imageQuery->where($whereClause);
<?php
-use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Auth\User;
use BookStack\Model;
use BookStack\Settings\SettingService;
}
// Check permission on ownable item
- $permissionService = app(PermissionService::class);
+ $permissions = app(PermissionApplicator::class);
- return $permissionService->checkOwnableUserAccess($ownable, $permission);
+ return $permissions->checkOwnableUserAccess($ownable, $permission);
}
/**
*/
function userCanOnAny(string $permission, string $entityClass = null): bool
{
- $permissionService = app(PermissionService::class);
+ $permissions = app(PermissionApplicator::class);
- return $permissionService->checkUserHasPermissionOnAnything($permission, $entityClass);
+ return $permissions->checkUserHasPermissionOnAnything($permission, $entityClass);
}
/**
]);
$token->save();
- app(JointPermissionBuilder::class)->buildJointPermissions();
+ app(JointPermissionBuilder::class)->rebuildForAll();
app(SearchIndex::class)->indexAllEntities();
}
}
$largeBook->chapters()->saveMany($chapters);
$all = array_merge([$largeBook], array_values($pages->all()), array_values($chapters->all()));
- app()->make(JointPermissionBuilder::class)->buildJointPermissionsForEntity($largeBook);
+ app()->make(JointPermissionBuilder::class)->rebuildForEntity($largeBook);
app()->make(SearchIndex::class)->indexEntities($all);
}
}
use BookStack\Auth\Permissions\JointPermission;
use BookStack\Entities\Models\Page;
+use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
{
public function test_regen_permissions_command()
{
- \DB::rollBack();
+ DB::rollBack();
JointPermission::query()->truncate();
$page = Page::first();
$this->assertDatabaseMissing('joint_permissions', ['entity_id' => $page->id]);
- $exitCode = \Artisan::call('bookstack:regenerate-permissions');
+ $exitCode = Artisan::call('bookstack:regenerate-permissions');
$this->assertTrue($exitCode === 0, 'Command executed successfully');
DB::beginTransaction();
foreach (RolePermission::all() as $perm) {
$publicRole->attachPermission($perm);
}
- $this->app->make(JointPermissionBuilder::class)->buildJointPermissionForRole($publicRole);
+ $this->app->make(JointPermissionBuilder::class)->rebuildForRole($publicRole);
/** @var Chapter $chapter */
$chapter = Chapter::query()->first();
namespace Tests;
use BookStack\Auth\Permissions\JointPermissionBuilder;
-use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Auth\Permissions\PermissionApplicator;
use BookStack\Auth\Permissions\PermissionsRepo;
use BookStack\Auth\Permissions\RolePermission;
use BookStack\Auth\Role;
$entity->save();
$entity->load('permissions');
- $this->app->make(JointPermissionBuilder::class)->buildJointPermissionsForEntity($entity);
+ $this->app->make(JointPermissionBuilder::class)->rebuildForEntity($entity);
$entity->load('jointPermissions');
}
/** @var Role $role */
foreach ($roles as $role) {
$role->detachPermission($permission);
- $permissionBuilder->buildJointPermissionForRole($role);
+ $permissionBuilder->rebuildForRole($role);
}
$user->clearPermissionCache();
$chapter = Chapter::factory()->create(array_merge(['book_id' => $book->id], $userAttrs));
$page = Page::factory()->create(array_merge(['book_id' => $book->id, 'chapter_id' => $chapter->id], $userAttrs));
- $this->app->make(JointPermissionBuilder::class)->buildJointPermissionsForEntity($book);
+ $this->app->make(JointPermissionBuilder::class)->rebuildForEntity($book);
return compact('book', 'chapter', 'page');
}