mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Fixed collapsed perm. gen for book sub-items.
Also converted the existing "JointPermission" usage to the new collapsed permission system.
This commit is contained in:
parent
7330139555
commit
451e4ac452
18
app/Auth/Permissions/CollapsedPermission.php
Normal file
18
app/Auth/Permissions/CollapsedPermission.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Auth\Permissions;
|
||||||
|
|
||||||
|
use BookStack\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property ?int $role_id
|
||||||
|
* @property ?int $user_id
|
||||||
|
* @property string $entity_type
|
||||||
|
* @property int $entity_id
|
||||||
|
* @property bool $view
|
||||||
|
*/
|
||||||
|
class CollapsedPermission extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'entity_permissions_collapsed';
|
||||||
|
}
|
@ -13,13 +13,13 @@ use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
|||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Joint permissions provide a pre-query "cached" table of view permissions for all core entity
|
* Collapsed permissions act as a "flattened" view of entity-level permissions in the system
|
||||||
* types for all roles in the system. This class generates out that table for different scenarios.
|
* so inheritance does not have to managed as part of permission querying.
|
||||||
*/
|
*/
|
||||||
class JointPermissionBuilder
|
class CollapsedPermissionBuilder
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Re-generate all entity permission from scratch.
|
* Re-generate all collapsed permissions from scratch.
|
||||||
*/
|
*/
|
||||||
public function rebuildForAll()
|
public function rebuildForAll()
|
||||||
{
|
{
|
||||||
@ -27,26 +27,26 @@ class JointPermissionBuilder
|
|||||||
|
|
||||||
// Chunk through all books
|
// Chunk through all books
|
||||||
$this->bookFetchQuery()->chunk(5, function (EloquentCollection $books) {
|
$this->bookFetchQuery()->chunk(5, function (EloquentCollection $books) {
|
||||||
$this->buildJointPermissionsForBooks($books);
|
$this->buildForBooks($books, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Chunk through all bookshelves
|
// Chunk through all bookshelves
|
||||||
Bookshelf::query()->withTrashed()
|
Bookshelf::query()->withTrashed()
|
||||||
->select(['id', 'owned_by'])
|
->select(['id'])
|
||||||
->chunk(50, function (EloquentCollection $shelves) {
|
->chunk(50, function (EloquentCollection $shelves) {
|
||||||
$this->generateCollapsedPermissions($shelves->all());
|
$this->generateCollapsedPermissions($shelves->all());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rebuild the entity jointPermissions for a particular entity.
|
* Rebuild the collapsed permissions for a particular entity.
|
||||||
*/
|
*/
|
||||||
public function rebuildForEntity(Entity $entity)
|
public function rebuildForEntity(Entity $entity)
|
||||||
{
|
{
|
||||||
$entities = [$entity];
|
$entities = [$entity];
|
||||||
if ($entity instanceof Book) {
|
if ($entity instanceof Book) {
|
||||||
$books = $this->bookFetchQuery()->where('id', '=', $entity->id)->get();
|
$books = $this->bookFetchQuery()->where('id', '=', $entity->id)->get();
|
||||||
$this->buildJointPermissionsForBooks($books, true);
|
$this->buildForBooks($books, true);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ class JointPermissionBuilder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->buildJointPermissionsForEntities($entities);
|
$this->buildForEntities($entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,20 +75,20 @@ class JointPermissionBuilder
|
|||||||
protected function bookFetchQuery(): Builder
|
protected function bookFetchQuery(): Builder
|
||||||
{
|
{
|
||||||
return Book::query()->withTrashed()
|
return Book::query()->withTrashed()
|
||||||
->select(['id', 'owned_by'])->with([
|
->select(['id'])->with([
|
||||||
'chapters' => function ($query) {
|
'chapters' => function ($query) {
|
||||||
$query->withTrashed()->select(['id', 'owned_by', 'book_id']);
|
$query->withTrashed()->select(['id', 'book_id']);
|
||||||
},
|
},
|
||||||
'pages' => function ($query) {
|
'pages' => function ($query) {
|
||||||
$query->withTrashed()->select(['id', 'owned_by', 'book_id', 'chapter_id']);
|
$query->withTrashed()->select(['id', 'book_id', 'chapter_id']);
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build joint permissions for the given book and role combinations.
|
* Build collapsed permissions for the given books.
|
||||||
*/
|
*/
|
||||||
protected function buildJointPermissionsForBooks(EloquentCollection $books, bool $deleteOld = false)
|
protected function buildForBooks(EloquentCollection $books, bool $deleteOld)
|
||||||
{
|
{
|
||||||
$entities = clone $books;
|
$entities = clone $books;
|
||||||
|
|
||||||
@ -103,27 +103,27 @@ class JointPermissionBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($deleteOld) {
|
if ($deleteOld) {
|
||||||
$this->deleteManyJointPermissionsForEntities($entities->all());
|
$this->deleteForEntities($entities->all());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->generateCollapsedPermissions($entities->all());
|
$this->generateCollapsedPermissions($entities->all());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rebuild the entity jointPermissions for a collection of entities.
|
* Rebuild the collapsed permissions for a collection of entities.
|
||||||
*/
|
*/
|
||||||
protected function buildJointPermissionsForEntities(array $entities)
|
protected function buildForEntities(array $entities)
|
||||||
{
|
{
|
||||||
$this->deleteManyJointPermissionsForEntities($entities);
|
$this->deleteForEntities($entities);
|
||||||
$this->generateCollapsedPermissions($entities);
|
$this->generateCollapsedPermissions($entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete all the entity jointPermissions for a list of entities.
|
* Delete the stored collapsed permissions for a list of entities.
|
||||||
*
|
*
|
||||||
* @param Entity[] $entities
|
* @param Entity[] $entities
|
||||||
*/
|
*/
|
||||||
protected function deleteManyJointPermissionsForEntities(array $entities)
|
protected function deleteForEntities(array $entities)
|
||||||
{
|
{
|
||||||
$simpleEntities = $this->entitiesToSimpleEntities($entities);
|
$simpleEntities = $this->entitiesToSimpleEntities($entities);
|
||||||
$idsByType = $this->entitiesToTypeIdMap($simpleEntities);
|
$idsByType = $this->entitiesToTypeIdMap($simpleEntities);
|
||||||
@ -141,6 +141,9 @@ class JointPermissionBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Convert the given list of entities into "SimpleEntityData" representations
|
||||||
|
* for faster usage and property access.
|
||||||
|
*
|
||||||
* @param Entity[] $entities
|
* @param Entity[] $entities
|
||||||
*
|
*
|
||||||
* @return SimpleEntityData[]
|
* @return SimpleEntityData[]
|
||||||
@ -154,7 +157,6 @@ class JointPermissionBuilder
|
|||||||
$simple = new SimpleEntityData();
|
$simple = new SimpleEntityData();
|
||||||
$simple->id = $attrs['id'];
|
$simple->id = $attrs['id'];
|
||||||
$simple->type = $entity->getMorphClass();
|
$simple->type = $entity->getMorphClass();
|
||||||
$simple->owned_by = $attrs['owned_by'] ?? 0;
|
|
||||||
$simple->book_id = $attrs['book_id'] ?? null;
|
$simple->book_id = $attrs['book_id'] ?? null;
|
||||||
$simple->chapter_id = $attrs['chapter_id'] ?? null;
|
$simple->chapter_id = $attrs['chapter_id'] ?? null;
|
||||||
$simpleEntities[] = $simple;
|
$simpleEntities[] = $simple;
|
||||||
@ -171,7 +173,7 @@ class JointPermissionBuilder
|
|||||||
protected function generateCollapsedPermissions(array $originalEntities)
|
protected function generateCollapsedPermissions(array $originalEntities)
|
||||||
{
|
{
|
||||||
$entities = $this->entitiesToSimpleEntities($originalEntities);
|
$entities = $this->entitiesToSimpleEntities($originalEntities);
|
||||||
$jointPermissions = [];
|
$collapsedPermData = [];
|
||||||
|
|
||||||
// Fetch related entity permissions
|
// Fetch related entity permissions
|
||||||
$permissions = $this->getEntityPermissionsForEntities($entities);
|
$permissions = $this->getEntityPermissionsForEntities($entities);
|
||||||
@ -181,12 +183,12 @@ class JointPermissionBuilder
|
|||||||
|
|
||||||
// Create Joint Permission Data
|
// Create Joint Permission Data
|
||||||
foreach ($entities as $entity) {
|
foreach ($entities as $entity) {
|
||||||
array_push($jointPermissions, ...$this->createCollapsedPermissionData($entity, $permissionMap));
|
array_push($collapsedPermData, ...$this->createCollapsedPermissionData($entity, $permissionMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::transaction(function () use ($jointPermissions) {
|
DB::transaction(function () use ($collapsedPermData) {
|
||||||
foreach (array_chunk($jointPermissions, 1000) as $jointPermissionChunk) {
|
foreach (array_chunk($collapsedPermData, 1000) as $dataChunk) {
|
||||||
DB::table('entity_permissions_collapsed')->insert($jointPermissionChunk);
|
DB::table('entity_permissions_collapsed')->insert($dataChunk);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -198,8 +200,8 @@ class JointPermissionBuilder
|
|||||||
{
|
{
|
||||||
$chain = [
|
$chain = [
|
||||||
$entity->type . ':' . $entity->id,
|
$entity->type . ':' . $entity->id,
|
||||||
$entity->chapter_id ? null : ('chapter:' . $entity->chapter_id),
|
$entity->chapter_id ? ('chapter:' . $entity->chapter_id) : null,
|
||||||
$entity->book_id ? null : ('book:' . $entity->book_id),
|
$entity->book_id ? ('book:' . $entity->book_id) : null,
|
||||||
];
|
];
|
||||||
|
|
||||||
$permissionData = [];
|
$permissionData = [];
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace BookStack\Auth\Permissions;
|
|
||||||
|
|
||||||
use BookStack\Auth\Role;
|
|
||||||
use BookStack\Entities\Models\Entity;
|
|
||||||
use BookStack\Model;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphOne;
|
|
||||||
|
|
||||||
class JointPermission extends Model
|
|
||||||
{
|
|
||||||
protected $primaryKey = null;
|
|
||||||
public $timestamps = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the role that this points to.
|
|
||||||
*/
|
|
||||||
public function role(): BelongsTo
|
|
||||||
{
|
|
||||||
return $this->belongsTo(Role::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the entity this points to.
|
|
||||||
*/
|
|
||||||
public function entity(): MorphOne
|
|
||||||
{
|
|
||||||
return $this->morphOne(Entity::class, 'entity');
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace BookStack\Auth\Permissions;
|
|
||||||
|
|
||||||
use BookStack\Entities\Models\Entity;
|
|
||||||
use BookStack\Model;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphOne;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds the "cached" user-specific permissions for entities in the system.
|
|
||||||
* These only exist to indicate resolved permissions active via user-specific
|
|
||||||
* entity permissions, not for all permission combinations for all users.
|
|
||||||
*
|
|
||||||
* @property int $user_id
|
|
||||||
* @property int $entity_id
|
|
||||||
* @property string $entity_type
|
|
||||||
* @property boolean $has_permission
|
|
||||||
*/
|
|
||||||
class JointUserPermission extends Model
|
|
||||||
{
|
|
||||||
protected $primaryKey = null;
|
|
||||||
public $timestamps = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the entity this points to.
|
|
||||||
*/
|
|
||||||
public function entity(): MorphOne
|
|
||||||
{
|
|
||||||
return $this->morphOne(Entity::class, 'entity');
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,13 +11,13 @@ use Illuminate\Database\Eloquent\Collection;
|
|||||||
|
|
||||||
class PermissionsRepo
|
class PermissionsRepo
|
||||||
{
|
{
|
||||||
protected JointPermissionBuilder $permissionBuilder;
|
protected CollapsedPermissionBuilder $permissionBuilder;
|
||||||
protected $systemRoles = ['admin', 'public'];
|
protected array $systemRoles = ['admin', 'public'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PermissionsRepo constructor.
|
* PermissionsRepo constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct(JointPermissionBuilder $permissionBuilder)
|
public function __construct(CollapsedPermissionBuilder $permissionBuilder)
|
||||||
{
|
{
|
||||||
$this->permissionBuilder = $permissionBuilder;
|
$this->permissionBuilder = $permissionBuilder;
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ class PermissionsRepo
|
|||||||
}
|
}
|
||||||
|
|
||||||
$role->entityPermissions()->delete();
|
$role->entityPermissions()->delete();
|
||||||
$role->jointPermissions()->delete();
|
$role->collapsedPermissions()->delete();
|
||||||
Activity::add(ActivityType::ROLE_DELETE, $role);
|
Activity::add(ActivityType::ROLE_DELETE, $role);
|
||||||
$role->delete();
|
$role->delete();
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ class SimpleEntityData
|
|||||||
{
|
{
|
||||||
public int $id;
|
public int $id;
|
||||||
public string $type;
|
public string $type;
|
||||||
public int $owned_by;
|
|
||||||
public ?int $book_id;
|
public ?int $book_id;
|
||||||
public ?int $chapter_id;
|
public ?int $chapter_id;
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace BookStack\Auth;
|
namespace BookStack\Auth;
|
||||||
|
|
||||||
|
use BookStack\Auth\Permissions\CollapsedPermission;
|
||||||
use BookStack\Auth\Permissions\EntityPermission;
|
use BookStack\Auth\Permissions\EntityPermission;
|
||||||
use BookStack\Auth\Permissions\JointPermission;
|
|
||||||
use BookStack\Auth\Permissions\RolePermission;
|
use BookStack\Auth\Permissions\RolePermission;
|
||||||
use BookStack\Interfaces\Loggable;
|
use BookStack\Interfaces\Loggable;
|
||||||
use BookStack\Model;
|
use BookStack\Model;
|
||||||
@ -39,14 +39,6 @@ class Role extends Model implements Loggable
|
|||||||
return $this->belongsToMany(User::class)->orderBy('name', 'asc');
|
return $this->belongsToMany(User::class)->orderBy('name', 'asc');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all related JointPermissions.
|
|
||||||
*/
|
|
||||||
public function jointPermissions(): HasMany
|
|
||||||
{
|
|
||||||
return $this->hasMany(JointPermission::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The RolePermissions that belong to the role.
|
* The RolePermissions that belong to the role.
|
||||||
*/
|
*/
|
||||||
@ -63,6 +55,14 @@ class Role extends Model implements Loggable
|
|||||||
return $this->hasMany(EntityPermission::class);
|
return $this->hasMany(EntityPermission::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all related entity collapsed permissions.
|
||||||
|
*/
|
||||||
|
public function collapsedPermissions(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(CollapsedPermission::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this role has a permission.
|
* Check if this role has a permission.
|
||||||
*/
|
*/
|
||||||
|
@ -5,6 +5,8 @@ namespace BookStack\Auth;
|
|||||||
use BookStack\Actions\Favourite;
|
use BookStack\Actions\Favourite;
|
||||||
use BookStack\Api\ApiToken;
|
use BookStack\Api\ApiToken;
|
||||||
use BookStack\Auth\Access\Mfa\MfaValue;
|
use BookStack\Auth\Access\Mfa\MfaValue;
|
||||||
|
use BookStack\Auth\Permissions\CollapsedPermission;
|
||||||
|
use BookStack\Auth\Permissions\EntityPermission;
|
||||||
use BookStack\Entities\Tools\SlugGenerator;
|
use BookStack\Entities\Tools\SlugGenerator;
|
||||||
use BookStack\Interfaces\Loggable;
|
use BookStack\Interfaces\Loggable;
|
||||||
use BookStack\Interfaces\Sluggable;
|
use BookStack\Interfaces\Sluggable;
|
||||||
@ -298,6 +300,22 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
|||||||
}, 'activities', 'users.id', '=', 'activities.user_id');
|
}, 'activities', 'users.id', '=', 'activities.user_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the entity permissions assigned to this specific user.
|
||||||
|
*/
|
||||||
|
public function entityPermissions(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(EntityPermission::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all related entity collapsed permissions.
|
||||||
|
*/
|
||||||
|
public function collapsedPermissions(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(CollapsedPermission::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the url for editing this user.
|
* Get the url for editing this user.
|
||||||
*/
|
*/
|
||||||
|
@ -153,6 +153,8 @@ class UserRepo
|
|||||||
$user->apiTokens()->delete();
|
$user->apiTokens()->delete();
|
||||||
$user->favourites()->delete();
|
$user->favourites()->delete();
|
||||||
$user->mfaValues()->delete();
|
$user->mfaValues()->delete();
|
||||||
|
$user->collapsedPermissions()->delete();
|
||||||
|
$user->entityPermissions()->delete();
|
||||||
$user->delete();
|
$user->delete();
|
||||||
|
|
||||||
// Delete user profile images
|
// Delete user profile images
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace BookStack\Console\Commands;
|
namespace BookStack\Console\Commands;
|
||||||
|
|
||||||
use BookStack\Auth\Permissions\JointPermissionBuilder;
|
use BookStack\Auth\Permissions\CollapsedPermissionBuilder;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
@ -22,12 +22,12 @@ class RegeneratePermissions extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Regenerate all system permissions';
|
protected $description = 'Regenerate all system permissions';
|
||||||
|
|
||||||
protected JointPermissionBuilder $permissionBuilder;
|
protected CollapsedPermissionBuilder $permissionBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command instance.
|
* Create a new command instance.
|
||||||
*/
|
*/
|
||||||
public function __construct(JointPermissionBuilder $permissionBuilder)
|
public function __construct(CollapsedPermissionBuilder $permissionBuilder)
|
||||||
{
|
{
|
||||||
$this->permissionBuilder = $permissionBuilder;
|
$this->permissionBuilder = $permissionBuilder;
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
@ -7,10 +7,9 @@ use BookStack\Actions\Comment;
|
|||||||
use BookStack\Actions\Favourite;
|
use BookStack\Actions\Favourite;
|
||||||
use BookStack\Actions\Tag;
|
use BookStack\Actions\Tag;
|
||||||
use BookStack\Actions\View;
|
use BookStack\Actions\View;
|
||||||
|
use BookStack\Auth\Permissions\CollapsedPermission;
|
||||||
use BookStack\Auth\Permissions\EntityPermission;
|
use BookStack\Auth\Permissions\EntityPermission;
|
||||||
use BookStack\Auth\Permissions\JointPermission;
|
use BookStack\Auth\Permissions\CollapsedPermissionBuilder;
|
||||||
use BookStack\Auth\Permissions\JointPermissionBuilder;
|
|
||||||
use BookStack\Auth\Permissions\JointUserPermission;
|
|
||||||
use BookStack\Auth\Permissions\PermissionApplicator;
|
use BookStack\Auth\Permissions\PermissionApplicator;
|
||||||
use BookStack\Entities\Tools\SlugGenerator;
|
use BookStack\Entities\Tools\SlugGenerator;
|
||||||
use BookStack\Interfaces\Deletable;
|
use BookStack\Interfaces\Deletable;
|
||||||
@ -188,19 +187,11 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the entity jointPermissions this is connected to.
|
* Get the entity collapsed permissions this is connected to.
|
||||||
*/
|
*/
|
||||||
public function jointPermissions(): MorphMany
|
public function collapsedPermissions(): MorphMany
|
||||||
{
|
{
|
||||||
return $this->morphMany(JointPermission::class, 'entity');
|
return $this->morphMany(CollapsedPermission::class, 'entity');
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the join user permissions for this entity.
|
|
||||||
*/
|
|
||||||
public function jointUserPermissions(): MorphMany
|
|
||||||
{
|
|
||||||
return $this->morphMany(JointUserPermission::class, 'entity');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -301,7 +292,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
|
|||||||
*/
|
*/
|
||||||
public function rebuildPermissions()
|
public function rebuildPermissions()
|
||||||
{
|
{
|
||||||
app()->make(JointPermissionBuilder::class)->rebuildForEntity(clone $this);
|
app()->make(CollapsedPermissionBuilder::class)->rebuildForEntity(clone $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -372,7 +372,7 @@ class TrashCan
|
|||||||
$entity->permissions()->delete();
|
$entity->permissions()->delete();
|
||||||
$entity->tags()->delete();
|
$entity->tags()->delete();
|
||||||
$entity->comments()->delete();
|
$entity->comments()->delete();
|
||||||
$entity->jointPermissions()->delete();
|
$entity->collapsedPermissions()->delete();
|
||||||
$entity->searchTerms()->delete();
|
$entity->searchTerms()->delete();
|
||||||
$entity->deletions()->delete();
|
$entity->deletions()->delete();
|
||||||
$entity->favourites()->delete();
|
$entity->favourites()->delete();
|
||||||
|
@ -55,8 +55,9 @@ function hasAppAccess(): bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the current user has a permission. If an ownable element
|
* Check if the current user has a permission.
|
||||||
* is passed in the jointPermissions are checked against that particular item.
|
* Checks a generic role permission or, if an ownable model is passed in, it will
|
||||||
|
* check against the given entity model, taking into account entity-level permissions.
|
||||||
*/
|
*/
|
||||||
function userCan(string $permission, Model $ownable = null): bool
|
function userCan(string $permission, Model $ownable = null): bool
|
||||||
{
|
{
|
||||||
|
@ -13,6 +13,9 @@ class CreateCollapsedRolePermissionsTable extends Migration
|
|||||||
*/
|
*/
|
||||||
public function up()
|
public function up()
|
||||||
{
|
{
|
||||||
|
// TODO - Drop joint permissions
|
||||||
|
// TODO - Run collapsed table rebuild.
|
||||||
|
|
||||||
Schema::create('entity_permissions_collapsed', function (Blueprint $table) {
|
Schema::create('entity_permissions_collapsed', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->unsignedInteger('role_id')->nullable();
|
$table->unsignedInteger('role_id')->nullable();
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
namespace Database\Seeders;
|
namespace Database\Seeders;
|
||||||
|
|
||||||
use BookStack\Api\ApiToken;
|
use BookStack\Api\ApiToken;
|
||||||
use BookStack\Auth\Permissions\JointPermissionBuilder;
|
use BookStack\Auth\Permissions\CollapsedPermissionBuilder;
|
||||||
use BookStack\Auth\Permissions\RolePermission;
|
use BookStack\Auth\Permissions\RolePermission;
|
||||||
use BookStack\Auth\Role;
|
use BookStack\Auth\Role;
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
@ -69,7 +69,7 @@ class DummyContentSeeder extends Seeder
|
|||||||
]);
|
]);
|
||||||
$token->save();
|
$token->save();
|
||||||
|
|
||||||
app(JointPermissionBuilder::class)->rebuildForAll();
|
app(CollapsedPermissionBuilder::class)->rebuildForAll();
|
||||||
app(SearchIndex::class)->indexAllEntities();
|
app(SearchIndex::class)->indexAllEntities();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Database\Seeders;
|
namespace Database\Seeders;
|
||||||
|
|
||||||
use BookStack\Auth\Permissions\JointPermissionBuilder;
|
use BookStack\Auth\Permissions\CollapsedPermissionBuilder;
|
||||||
use BookStack\Auth\Role;
|
use BookStack\Auth\Role;
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
use BookStack\Entities\Models\Book;
|
use BookStack\Entities\Models\Book;
|
||||||
@ -35,7 +35,7 @@ class LargeContentSeeder extends Seeder
|
|||||||
$largeBook->chapters()->saveMany($chapters);
|
$largeBook->chapters()->saveMany($chapters);
|
||||||
$all = array_merge([$largeBook], array_values($pages->all()), array_values($chapters->all()));
|
$all = array_merge([$largeBook], array_values($pages->all()), array_values($chapters->all()));
|
||||||
|
|
||||||
app()->make(JointPermissionBuilder::class)->rebuildForEntity($largeBook);
|
app()->make(CollapsedPermissionBuilder::class)->rebuildForEntity($largeBook);
|
||||||
app()->make(SearchIndex::class)->indexEntities($all);
|
app()->make(SearchIndex::class)->indexEntities($all);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
namespace Tests\Commands;
|
namespace Tests\Commands;
|
||||||
|
|
||||||
use BookStack\Auth\Permissions\JointPermission;
|
use BookStack\Auth\Permissions\CollapsedPermission;
|
||||||
use BookStack\Entities\Models\Page;
|
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
@ -13,15 +12,23 @@ class RegeneratePermissionsCommandTest extends TestCase
|
|||||||
public function test_regen_permissions_command()
|
public function test_regen_permissions_command()
|
||||||
{
|
{
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
JointPermission::query()->truncate();
|
$page = $this->entities->page();
|
||||||
$page = Page::first();
|
$editor = $this->users->editor();
|
||||||
|
$this->permissions->addEntityPermission($page, ['view'], null, $editor);
|
||||||
|
CollapsedPermission::query()->truncate();
|
||||||
|
|
||||||
$this->assertDatabaseMissing('joint_permissions', ['entity_id' => $page->id]);
|
$this->assertDatabaseMissing('entity_permissions_collapsed', ['entity_id' => $page->id]);
|
||||||
|
|
||||||
$exitCode = Artisan::call('bookstack:regenerate-permissions');
|
$exitCode = Artisan::call('bookstack:regenerate-permissions');
|
||||||
$this->assertTrue($exitCode === 0, 'Command executed successfully');
|
$this->assertTrue($exitCode === 0, 'Command executed successfully');
|
||||||
DB::beginTransaction();
|
|
||||||
|
|
||||||
$this->assertDatabaseHas('joint_permissions', ['entity_id' => $page->id]);
|
$this->assertDatabaseHas('entity_permissions_collapsed', [
|
||||||
|
'entity_id' => $page->id,
|
||||||
|
'user_id' => $editor->id,
|
||||||
|
'view' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
CollapsedPermission::query()->truncate();
|
||||||
|
DB::beginTransaction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace Tests\Helpers;
|
namespace Tests\Helpers;
|
||||||
|
|
||||||
use BookStack\Auth\Permissions\EntityPermission;
|
use BookStack\Auth\Permissions\EntityPermission;
|
||||||
use BookStack\Auth\Permissions\JointPermissionBuilder;
|
|
||||||
use BookStack\Auth\Permissions\RolePermission;
|
use BookStack\Auth\Permissions\RolePermission;
|
||||||
use BookStack\Auth\Role;
|
use BookStack\Auth\Role;
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
@ -70,7 +69,6 @@ class PermissionsProvider
|
|||||||
public function regenerateForEntity(Entity $entity): void
|
public function regenerateForEntity(Entity $entity): void
|
||||||
{
|
{
|
||||||
$entity->rebuildPermissions();
|
$entity->rebuildPermissions();
|
||||||
$entity->load('jointPermissions');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
use BookStack\Auth\Permissions\JointPermissionBuilder;
|
|
||||||
use BookStack\Auth\Permissions\RolePermission;
|
use BookStack\Auth\Permissions\RolePermission;
|
||||||
use BookStack\Auth\Role;
|
use BookStack\Auth\Role;
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
|
Loading…
Reference in New Issue
Block a user