Started removal of non-view permission queries

Updated ajax search and entity selector usage to display and handle
items that the user does not have permission to interact with.
Started logic changes to not allow permission type to be passed around,
with views instead being the fixed sole permission.
This commit is contained in:
Dan Brown 2022-07-13 15:23:03 +01:00
parent 2989852520
commit 4fb85a9a5c
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
10 changed files with 47 additions and 15 deletions

View File

@ -72,6 +72,7 @@ class PermissionApplicator
$action = $permission;
}
// TODO - Use a non-query based check
$hasAccess = $this->entityRestrictionQuery($baseQuery, $action)->count() > 0;
$this->clean();
@ -163,14 +164,14 @@ class PermissionApplicator
/**
* Add restrictions for a generic entity.
*/
public function enforceEntityRestrictions(Entity $entity, Builder $query, string $action = 'view'): Builder
public function enforceEntityRestrictions(Entity $entity, Builder $query): Builder
{
if ($entity instanceof Page) {
// Prevent drafts being visible to others.
$this->enforceDraftVisibilityOnQuery($query);
}
return $this->entityRestrictionQuery($query, $action);
return $this->entityRestrictionQuery($query, 'view');
}
/**

View File

@ -163,7 +163,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
}
/**
* Get all permissions belonging to a the current user.
* Get all permissions belonging to the current user.
*/
protected function permissions(): Collection
{

View File

@ -7,10 +7,10 @@ use Illuminate\Support\Facades\DB;
class Popular extends EntityQuery
{
public function run(int $count, int $page, array $filterModels = null, string $action = 'view')
public function run(int $count, int $page, array $filterModels = null)
{
$query = $this->permissionService()
->filterRestrictedEntityRelations(View::query(), 'views', 'viewable_id', 'viewable_type', $action)
->filterRestrictedEntityRelations(View::query(), 'views', 'viewable_id', 'viewable_type', 'view')
->select('*', 'viewable_id', 'viewable_type', DB::raw('SUM(views) as view_count'))
->groupBy('viewable_id', 'viewable_type')
->orderBy('view_count', 'desc');

View File

@ -54,7 +54,7 @@ class SearchRunner
*
* @return array{total: int, count: int, has_more: bool, results: Entity[]}
*/
public function searchEntities(SearchOptions $searchOpts, string $entityType = 'all', int $page = 1, int $count = 20, string $action = 'view'): array
public function searchEntities(SearchOptions $searchOpts, string $entityType = 'all', int $page = 1, int $count = 20): array
{
$entityTypes = array_keys($this->entityProvider->all());
$entityTypesToSearch = $entityTypes;
@ -75,7 +75,7 @@ class SearchRunner
}
$entityModelInstance = $this->entityProvider->get($entityType);
$searchQuery = $this->buildQuery($searchOpts, $entityModelInstance, $action);
$searchQuery = $this->buildQuery($searchOpts, $entityModelInstance);
$entityTotal = $searchQuery->count();
$searchResults = $this->getPageOfDataFromQuery($searchQuery, $entityModelInstance, $page, $count);
@ -159,7 +159,7 @@ class SearchRunner
/**
* Create a search query for an entity.
*/
protected function buildQuery(SearchOptions $searchOpts, Entity $entityModelInstance, string $action = 'view'): EloquentBuilder
protected function buildQuery(SearchOptions $searchOpts, Entity $entityModelInstance): EloquentBuilder
{
$entityQuery = $entityModelInstance->newQuery();
@ -193,7 +193,7 @@ class SearchRunner
}
}
return $this->permissions->enforceEntityRestrictions($entityModelInstance, $entityQuery, $action);
return $this->permissions->enforceEntityRestrictions($entityModelInstance, $entityQuery);
}
/**

View File

@ -12,7 +12,6 @@ use Illuminate\Http\Request;
class SearchController extends Controller
{
protected $searchRunner;
protected $entityContextManager;
public function __construct(SearchRunner $searchRunner)
{
@ -79,12 +78,12 @@ class SearchController extends Controller
// Search for entities otherwise show most popular
if ($searchTerm !== false) {
$searchTerm .= ' {type:' . implode('|', $entityTypes) . '}';
$entities = $this->searchRunner->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 20, $permission)['results'];
$entities = $this->searchRunner->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 20)['results'];
} else {
$entities = (new Popular())->run(20, 0, $entityTypes, $permission);
$entities = (new Popular())->run(20, 0, $entityTypes);
}
return view('search.parts.entity-ajax-list', ['entities' => $entities]);
return view('search.parts.entity-ajax-list', ['entities' => $entities, 'permission' => $permission]);
}
/**

View File

@ -443,6 +443,14 @@ ul.pagination {
}
}
.entity-list-item.disabled {
pointer-events: none;
cursor: not-allowed;
opacity: 0.8;
user-select: none;
background: var(--bg-disabled);
}
.entity-list-item-path-sep {
display: inline-block;
vertical-align: top;

View File

@ -45,6 +45,12 @@ $fs-s: 12px;
--color-chapter: #af4d0d;
--color-book: #077b70;
--color-bookshelf: #a94747;
--bg-disabled: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='100%25' width='100%25'%3E%3Cdefs%3E%3Cpattern id='doodad' width='19' height='19' viewBox='0 0 40 40' patternUnits='userSpaceOnUse' patternTransform='rotate(143)'%3E%3Crect width='100%25' height='100%25' fill='rgba(42, 67, 101,0)'/%3E%3Cpath d='M-10 30h60v20h-60zM-10-10h60v20h-60' fill='rgba(26, 32, 44,0)'/%3E%3Cpath d='M-10 10h60v20h-60zM-10-30h60v20h-60z' fill='rgba(0, 0, 0,0.05)'/%3E%3C/pattern%3E%3C/defs%3E%3Crect fill='url(%23doodad)' height='200%25' width='200%25'/%3E%3C/svg%3E");
}
:root.dark-mode {
--bg-disabled: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='100%25' width='100%25'%3E%3Cdefs%3E%3Cpattern id='doodad' width='19' height='19' viewBox='0 0 40 40' patternUnits='userSpaceOnUse' patternTransform='rotate(143)'%3E%3Crect width='100%25' height='100%25' fill='rgba(42, 67, 101,0)'/%3E%3Cpath d='M-10 30h60v20h-60zM-10-10h60v20h-60' fill='rgba(26, 32, 44,0)'/%3E%3Cpath d='M-10 10h60v20h-60zM-10-30h60v20h-60z' fill='rgba(255, 255, 255,0.05)'/%3E%3C/pattern%3E%3C/defs%3E%3Crect fill='url(%23doodad)' height='200%25' width='200%25'/%3E%3C/svg%3E");
}
$positive: #0f7d15;

View File

@ -1,7 +1,13 @@
@component('entities.list-item-basic', ['entity' => $entity])
@component('entities.list-item-basic', ['entity' => $entity, 'classes' => (($locked ?? false) ? 'disabled ' : '') . ($classes ?? '') ])
<div class="entity-item-snippet">
@if($locked ?? false)
<div class="text-warn my-xxs bold">
@icon('lock')You don't have the required permissions to select this item.
</div>
@endif
@if($showPath ?? false)
@if($entity->relationLoaded('book') && $entity->book)
<span class="text-book">{{ $entity->book->getShortName(42) }}</span>

View File

@ -2,7 +2,12 @@
@if(count($entities) > 0)
@foreach($entities as $index => $entity)
@include('entities.list-item', ['entity' => $entity, 'showPath' => true])
@include('entities.list-item', [
'entity' => $entity,
'showPath' => true,
'locked' => $permission !== 'view' && !userCan($permission, $entity)
])
@if($index !== count($entities) - 1)
<hr>
@endif

View File

@ -38,6 +38,13 @@ use Illuminate\View\Middleware\ShareErrorsFromSession;
Route::get('/status', [StatusController::class, 'show']);
Route::get('/robots.txt', [HomeController::class, 'robots']);
Route::get('/test', function() {
$book = \BookStack\Entities\Models\Book::query()->where('slug', '=', 'k5TrhXxaNb')->firstOrFail();
$builder= app()->make(\BookStack\Auth\Permissions\JointPermissionBuilder::class);
$builder->rebuildForEntity($book);
return 'finished';
})->withoutMiddleware('web');
// Authenticated routes...
Route::middleware('auth')->group(function () {