Dan Brown

Allowed child entity permissions to override parent permissions

Updated elements of a page display and sidebar render to allow
child permissions to work even when parent entitites have permission
set. This allows a page with a 'view' permission to be viewable even
when the parent book or chapter is not viewable.

Fixes #366
...@@ -348,6 +348,10 @@ class EntityRepo ...@@ -348,6 +348,10 @@ class EntityRepo
348 foreach ($entities as $entity) { 348 foreach ($entities as $entity) {
349 if ($entity->chapter_id === 0 || $entity->chapter_id === '0') continue; 349 if ($entity->chapter_id === 0 || $entity->chapter_id === '0') continue;
350 $parentKey = 'BookStack\\Chapter:' . $entity->chapter_id; 350 $parentKey = 'BookStack\\Chapter:' . $entity->chapter_id;
351 + if (!isset($parents[$parentKey])) {
352 + $tree[] = $entity;
353 + continue;
354 + }
351 $chapter = $parents[$parentKey]; 355 $chapter = $parents[$parentKey];
352 $chapter->pages->push($entity); 356 $chapter->pages->push($entity);
353 } 357 }
......
1 <div class="breadcrumbs"> 1 <div class="breadcrumbs">
2 + @if (userCan('view', $book))
2 <a href="{{ $chapter->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $chapter->book->getShortName() }}</a> 3 <a href="{{ $chapter->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $chapter->book->getShortName() }}</a>
3 <span class="sep">&raquo;</span> 4 <span class="sep">&raquo;</span>
5 + @endif
4 <a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{$chapter->getShortName()}}</a> 6 <a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{$chapter->getShortName()}}</a>
5 </div> 7 </div>
...\ No newline at end of file ...\ No newline at end of file
......
1 <div class="breadcrumbs"> 1 <div class="breadcrumbs">
2 - <a href="{{ $page->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName() }}</a> 2 + @if (userCan('view', $page->book))
3 - @if($page->hasChapter()) 3 + <a href="{{ $page->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName() }}</a>
4 <span class="sep">&raquo;</span> 4 <span class="sep">&raquo;</span>
5 + @endif
6 + @if($page->hasChapter() && userCan('view', $page->chapter))
5 <a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button"> 7 <a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
6 <i class="zmdi zmdi-collection-bookmark"></i> 8 <i class="zmdi zmdi-collection-bookmark"></i>
7 {{ $page->chapter->getShortName() }} 9 {{ $page->chapter->getShortName() }}
8 </a> 10 </a>
11 + <span class="sep">&raquo;</span>
9 @endif 12 @endif
10 - <span class="sep">&raquo;</span>
11 <a href="{{ $page->getUrl() }}" class="text-page text-button"><i class="zmdi zmdi-file"></i>{{ $page->getShortName() }}</a> 13 <a href="{{ $page->getUrl() }}" class="text-page text-button"><i class="zmdi zmdi-file"></i>{{ $page->getShortName() }}</a>
12 </div> 14 </div>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -39,8 +39,10 @@ ...@@ -39,8 +39,10 @@
39 39
40 <h6 class="text-muted">{{ trans('entities.books_navigation') }}</h6> 40 <h6 class="text-muted">{{ trans('entities.books_navigation') }}</h6>
41 <ul class="sidebar-page-list menu"> 41 <ul class="sidebar-page-list menu">
42 - <li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
43 42
43 + @if (userCan('view', $book))
44 + <li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
45 + @endif
44 46
45 @foreach($sidebarTree as $bookChild) 47 @foreach($sidebarTree as $bookChild)
46 <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}"> 48 <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
......
...@@ -522,4 +522,21 @@ class RestrictionsTest extends BrowserKitTest ...@@ -522,4 +522,21 @@ class RestrictionsTest extends BrowserKitTest
522 ->see('Delete Chapter'); 522 ->see('Delete Chapter');
523 } 523 }
524 524
525 + public function test_page_visible_if_has_permissions_when_book_not_visible()
526 + {
527 + $book = \BookStack\Book::first();
528 + $bookChapter = $book->chapters->first();
529 + $bookPage = $bookChapter->pages->first();
530 +
531 + $this->setEntityRestrictions($book, []);
532 + $this->setEntityRestrictions($bookPage, ['view']);
533 +
534 + $this->actingAs($this->viewer);
535 + $this->get($bookPage->getUrl());
536 + $this->assertResponseOk();
537 + $this->see($bookPage->name);
538 + $this->dontSee(substr($book->name, 0, 15));
539 + $this->dontSee(substr($bookChapter->name, 0, 15));
540 + }
541 +
525 } 542 }
......