]> BookStack Code Mirror - bookstack/blob - tests/Permissions/EntityPermissionsTest.php
6ea0257b81e5c1d6dced3c8c279c837e181fb122
[bookstack] / tests / Permissions / EntityPermissionsTest.php
1 <?php
2
3 namespace Tests\Permissions;
4
5 use BookStack\Entities\Models\Book;
6 use BookStack\Entities\Models\Bookshelf;
7 use BookStack\Entities\Models\Chapter;
8 use BookStack\Entities\Models\Entity;
9 use BookStack\Entities\Models\Page;
10 use BookStack\Users\Models\Role;
11 use BookStack\Users\Models\User;
12 use Exception;
13 use Illuminate\Support\Str;
14 use Tests\TestCase;
15
16 class EntityPermissionsTest extends TestCase
17 {
18     protected User $user;
19     protected User $viewer;
20
21     protected function setUp(): void
22     {
23         parent::setUp();
24         $this->user = $this->users->editor();
25         $this->viewer = $this->users->viewer();
26     }
27
28     protected function setRestrictionsForTestRoles(Entity $entity, array $actions = [])
29     {
30         $roles = [
31             $this->user->roles->first(),
32             $this->viewer->roles->first(),
33         ];
34         $this->permissions->setEntityPermissions($entity, $actions, $roles);
35     }
36
37     public function test_bookshelf_view_restriction()
38     {
39         $shelf = $this->entities->shelf();
40
41         $this->actingAs($this->user)
42             ->get($shelf->getUrl())
43             ->assertStatus(200);
44
45         $this->setRestrictionsForTestRoles($shelf, []);
46
47         $this->followingRedirects()->get($shelf->getUrl())
48             ->assertSee('Shelf not found');
49
50         $this->setRestrictionsForTestRoles($shelf, ['view']);
51
52         $this->get($shelf->getUrl())
53             ->assertSee($shelf->name);
54     }
55
56     public function test_bookshelf_update_restriction()
57     {
58         $shelf = $this->entities->shelf();
59
60         $this->actingAs($this->user)
61             ->get($shelf->getUrl('/edit'))
62             ->assertSee('Edit Shelf');
63
64         $this->setRestrictionsForTestRoles($shelf, ['view', 'delete']);
65
66         $resp = $this->get($shelf->getUrl('/edit'))
67             ->assertRedirect('/');
68         $this->followRedirects($resp)->assertSee('You do not have permission');
69
70         $this->setRestrictionsForTestRoles($shelf, ['view', 'update']);
71
72         $this->get($shelf->getUrl('/edit'))
73             ->assertOk();
74     }
75
76     public function test_bookshelf_delete_restriction()
77     {
78         $shelf = $this->entities->shelf();
79
80         $this->actingAs($this->user)
81             ->get($shelf->getUrl('/delete'))
82             ->assertSee('Delete Shelf');
83
84         $this->setRestrictionsForTestRoles($shelf, ['view', 'update']);
85
86         $this->get($shelf->getUrl('/delete'))->assertRedirect('/');
87         $this->get('/')->assertSee('You do not have permission');
88
89         $this->setRestrictionsForTestRoles($shelf, ['view', 'delete']);
90
91         $this->get($shelf->getUrl('/delete'))
92             ->assertOk()
93             ->assertSee('Delete Shelf');
94     }
95
96     public function test_book_view_restriction()
97     {
98         $book = $this->entities->book();
99         $bookPage = $book->pages->first();
100         $bookChapter = $book->chapters->first();
101
102         $bookUrl = $book->getUrl();
103         $this->actingAs($this->user)
104             ->get($bookUrl)
105             ->assertOk();
106
107         $this->setRestrictionsForTestRoles($book, []);
108
109         $this->followingRedirects()->get($bookUrl)
110             ->assertSee('Book not found');
111         $this->followingRedirects()->get($bookPage->getUrl())
112             ->assertSee('Page not found');
113         $this->followingRedirects()->get($bookChapter->getUrl())
114             ->assertSee('Chapter not found');
115
116         $this->setRestrictionsForTestRoles($book, ['view']);
117
118         $this->get($bookUrl)
119             ->assertSee($book->name);
120         $this->get($bookPage->getUrl())
121             ->assertSee($bookPage->name);
122         $this->get($bookChapter->getUrl())
123             ->assertSee($bookChapter->name);
124     }
125
126     public function test_book_create_restriction()
127     {
128         $book = $this->entities->book();
129
130         $bookUrl = $book->getUrl();
131         $resp = $this->actingAs($this->viewer)->get($bookUrl);
132         $this->withHtml($resp)->assertElementNotContains('.actions', 'New Page')
133             ->assertElementNotContains('.actions', 'New Chapter');
134         $resp = $this->actingAs($this->user)->get($bookUrl);
135         $this->withHtml($resp)->assertElementContains('.actions', 'New Page')
136             ->assertElementContains('.actions', 'New Chapter');
137
138         $this->setRestrictionsForTestRoles($book, ['view', 'delete', 'update']);
139
140         $this->get($bookUrl . '/create-chapter')->assertRedirect('/');
141         $this->get('/')->assertSee('You do not have permission');
142
143         $this->get($bookUrl . '/create-page')->assertRedirect('/');
144         $this->get('/')->assertSee('You do not have permission');
145
146         $resp = $this->get($bookUrl);
147         $this->withHtml($resp)->assertElementNotContains('.actions', 'New Page')
148             ->assertElementNotContains('.actions', 'New Chapter');
149
150         $this->setRestrictionsForTestRoles($book, ['view', 'create']);
151
152         $resp = $this->post($book->getUrl('/create-chapter'), [
153             'name'        => 'test chapter',
154             'description' => 'desc',
155         ]);
156         $resp->assertRedirect($book->getUrl('/chapter/test-chapter'));
157
158         $this->get($book->getUrl('/create-page'));
159         /** @var Page $page */
160         $page = Page::query()->where('draft', '=', true)->orderBy('id', 'desc')->first();
161         $resp = $this->post($page->getUrl(), [
162             'name' => 'test page',
163             'html' => 'test content',
164         ]);
165         $resp->assertRedirect($book->getUrl('/page/test-page'));
166
167         $resp = $this->get($bookUrl);
168         $this->withHtml($resp)->assertElementContains('.actions', 'New Page')
169             ->assertElementContains('.actions', 'New Chapter');
170     }
171
172     public function test_book_update_restriction()
173     {
174         $book = $this->entities->book();
175         $bookPage = $book->pages->first();
176         $bookChapter = $book->chapters->first();
177
178         $bookUrl = $book->getUrl();
179         $this->actingAs($this->user)
180             ->get($bookUrl . '/edit')
181             ->assertSee('Edit Book');
182
183         $this->setRestrictionsForTestRoles($book, ['view', 'delete']);
184
185         $this->get($bookUrl . '/edit')->assertRedirect('/');
186         $this->get('/')->assertSee('You do not have permission');
187         $this->get($bookPage->getUrl() . '/edit')->assertRedirect('/');
188         $this->get('/')->assertSee('You do not have permission');
189         $this->get($bookChapter->getUrl() . '/edit')->assertRedirect('/');
190         $this->get('/')->assertSee('You do not have permission');
191
192         $this->setRestrictionsForTestRoles($book, ['view', 'update']);
193
194         $this->get($bookUrl . '/edit')->assertOk();
195         $this->get($bookPage->getUrl() . '/edit')->assertOk();
196         $this->get($bookChapter->getUrl() . '/edit')->assertSee('Edit Chapter');
197     }
198
199     public function test_book_delete_restriction()
200     {
201         $book = $this->entities->book();
202         $bookPage = $book->pages->first();
203         $bookChapter = $book->chapters->first();
204
205         $bookUrl = $book->getUrl();
206         $this->actingAs($this->user)->get($bookUrl . '/delete')
207             ->assertSee('Delete Book');
208
209         $this->setRestrictionsForTestRoles($book, ['view', 'update']);
210
211         $this->get($bookUrl . '/delete')->assertRedirect('/');
212         $this->get('/')->assertSee('You do not have permission');
213         $this->get($bookPage->getUrl() . '/delete')->assertRedirect('/');
214         $this->get('/')->assertSee('You do not have permission');
215         $this->get($bookChapter->getUrl() . '/delete')->assertRedirect('/');
216         $this->get('/')->assertSee('You do not have permission');
217
218         $this->setRestrictionsForTestRoles($book, ['view', 'delete']);
219
220         $this->get($bookUrl . '/delete')->assertOk()->assertSee('Delete Book');
221         $this->get($bookPage->getUrl('/delete'))->assertOk()->assertSee('Delete Page');
222         $this->get($bookChapter->getUrl('/delete'))->assertSee('Delete Chapter');
223     }
224
225     public function test_chapter_view_restriction()
226     {
227         $chapter = $this->entities->chapter();
228         $chapterPage = $chapter->pages->first();
229
230         $chapterUrl = $chapter->getUrl();
231         $this->actingAs($this->user)->get($chapterUrl)->assertOk();
232
233         $this->setRestrictionsForTestRoles($chapter, []);
234
235         $this->followingRedirects()->get($chapterUrl)->assertSee('Chapter not found');
236         $this->followingRedirects()->get($chapterPage->getUrl())->assertSee('Page not found');
237
238         $this->setRestrictionsForTestRoles($chapter, ['view']);
239
240         $this->get($chapterUrl)->assertSee($chapter->name);
241         $this->get($chapterPage->getUrl())->assertSee($chapterPage->name);
242     }
243
244     public function test_chapter_create_restriction()
245     {
246         $chapter = $this->entities->chapter();
247
248         $chapterUrl = $chapter->getUrl();
249         $resp = $this->actingAs($this->user)->get($chapterUrl);
250         $this->withHtml($resp)->assertElementContains('.actions', 'New Page');
251
252         $this->setRestrictionsForTestRoles($chapter, ['view', 'delete', 'update']);
253
254         $this->get($chapterUrl . '/create-page')->assertRedirect('/');
255         $this->get('/')->assertSee('You do not have permission');
256         $this->withHtml($this->get($chapterUrl))->assertElementNotContains('.actions', 'New Page');
257
258         $this->setRestrictionsForTestRoles($chapter, ['view', 'create']);
259
260         $this->get($chapter->getUrl('/create-page'));
261         /** @var Page $page */
262         $page = Page::query()->where('draft', '=', true)->orderBy('id', 'desc')->first();
263         $resp = $this->post($page->getUrl(), [
264             'name' => 'test page',
265             'html' => 'test content',
266         ]);
267         $resp->assertRedirect($chapter->book->getUrl('/page/test-page'));
268
269         $this->withHtml($this->get($chapterUrl))->assertElementContains('.actions', 'New Page');
270     }
271
272     public function test_chapter_update_restriction()
273     {
274         $chapter = $this->entities->chapter();
275         $chapterPage = $chapter->pages->first();
276
277         $chapterUrl = $chapter->getUrl();
278         $this->actingAs($this->user)->get($chapterUrl . '/edit')
279             ->assertSee('Edit Chapter');
280
281         $this->setRestrictionsForTestRoles($chapter, ['view', 'delete']);
282
283         $this->get($chapterUrl . '/edit')->assertRedirect('/');
284         $this->get('/')->assertSee('You do not have permission');
285         $this->get($chapterPage->getUrl() . '/edit')->assertRedirect('/');
286         $this->get('/')->assertSee('You do not have permission');
287
288         $this->setRestrictionsForTestRoles($chapter, ['view', 'update']);
289
290         $this->get($chapterUrl . '/edit')->assertOk()->assertSee('Edit Chapter');
291         $this->get($chapterPage->getUrl() . '/edit')->assertOk();
292     }
293
294     public function test_chapter_delete_restriction()
295     {
296         $chapter = $this->entities->chapter();
297         $chapterPage = $chapter->pages->first();
298
299         $chapterUrl = $chapter->getUrl();
300         $this->actingAs($this->user)
301             ->get($chapterUrl . '/delete')
302             ->assertSee('Delete Chapter');
303
304         $this->setRestrictionsForTestRoles($chapter, ['view', 'update']);
305
306         $this->get($chapterUrl . '/delete')->assertRedirect('/');
307         $this->get('/')->assertSee('You do not have permission');
308         $this->get($chapterPage->getUrl() . '/delete')->assertRedirect('/');
309         $this->get('/')->assertSee('You do not have permission');
310
311         $this->setRestrictionsForTestRoles($chapter, ['view', 'delete']);
312
313         $this->get($chapterUrl . '/delete')->assertOk()->assertSee('Delete Chapter');
314         $this->get($chapterPage->getUrl() . '/delete')->assertOk()->assertSee('Delete Page');
315     }
316
317     public function test_page_view_restriction()
318     {
319         $page = $this->entities->page();
320
321         $pageUrl = $page->getUrl();
322         $this->actingAs($this->user)->get($pageUrl)->assertOk();
323
324         $this->setRestrictionsForTestRoles($page, ['update', 'delete']);
325
326         $this->get($pageUrl)->assertSee('Page not found');
327
328         $this->setRestrictionsForTestRoles($page, ['view']);
329
330         $this->get($pageUrl)->assertSee($page->name);
331     }
332
333     public function test_page_update_restriction()
334     {
335         $page = $this->entities->page();
336
337         $pageUrl = $page->getUrl();
338         $resp = $this->actingAs($this->user)
339             ->get($pageUrl . '/edit');
340         $this->withHtml($resp)->assertElementExists('input[name="name"][value="' . $page->name . '"]');
341
342         $this->setRestrictionsForTestRoles($page, ['view', 'delete']);
343
344         $this->get($pageUrl . '/edit')->assertRedirect('/');
345         $this->get('/')->assertSee('You do not have permission');
346
347         $this->setRestrictionsForTestRoles($page, ['view', 'update']);
348
349         $resp = $this->get($pageUrl . '/edit')
350             ->assertOk();
351         $this->withHtml($resp)->assertElementExists('input[name="name"][value="' . $page->name . '"]');
352     }
353
354     public function test_page_delete_restriction()
355     {
356         $page = $this->entities->page();
357
358         $pageUrl = $page->getUrl();
359         $this->actingAs($this->user)
360             ->get($pageUrl . '/delete')
361             ->assertSee('Delete Page');
362
363         $this->setRestrictionsForTestRoles($page, ['view', 'update']);
364
365         $this->get($pageUrl . '/delete')->assertRedirect('/');
366         $this->get('/')->assertSee('You do not have permission');
367
368         $this->setRestrictionsForTestRoles($page, ['view', 'delete']);
369
370         $this->get($pageUrl . '/delete')->assertOk()->assertSee('Delete Page');
371     }
372
373     protected function entityRestrictionFormTest(string $model, string $title, string $permission, string $roleId)
374     {
375         /** @var Entity $modelInstance */
376         $modelInstance = $model::query()->first();
377         $this->asAdmin()->get($modelInstance->getUrl('/permissions'))
378             ->assertSee($title);
379
380         $this->put($modelInstance->getUrl('/permissions'), [
381             'permissions' => [
382                 $roleId => [
383                     $permission => 'true',
384                 ],
385             ],
386         ]);
387
388         $this->assertDatabaseHas('entity_permissions', [
389             'entity_id'      => $modelInstance->id,
390             'entity_type'    => $modelInstance->getMorphClass(),
391             'role_id'        => $roleId,
392             $permission => true,
393         ]);
394     }
395
396     public function test_bookshelf_restriction_form()
397     {
398         $this->entityRestrictionFormTest(Bookshelf::class, 'Shelf Permissions', 'view', '2');
399     }
400
401     public function test_book_restriction_form()
402     {
403         $this->entityRestrictionFormTest(Book::class, 'Book Permissions', 'view', '2');
404     }
405
406     public function test_chapter_restriction_form()
407     {
408         $this->entityRestrictionFormTest(Chapter::class, 'Chapter Permissions', 'update', '2');
409     }
410
411     public function test_page_restriction_form()
412     {
413         $this->entityRestrictionFormTest(Page::class, 'Page Permissions', 'delete', '2');
414     }
415
416     public function test_shelf_create_permission_visible_with_notice()
417     {
418         $shelf = $this->entities->shelf();
419
420         $resp = $this->asAdmin()->get($shelf->getUrl('/permissions'));
421         $html = $this->withHtml($resp);
422         $html->assertElementExists('input[name$="[create]"]');
423         $resp->assertSee('Shelf create permissions are only used for copying permissions to child books using the action below.');
424     }
425
426     public function test_restricted_pages_not_visible_in_book_navigation_on_pages()
427     {
428         $chapter = $this->entities->chapter();
429         $page = $chapter->pages->first();
430         $page2 = $chapter->pages[2];
431
432         $this->setRestrictionsForTestRoles($page, []);
433
434         $resp = $this->actingAs($this->user)->get($page2->getUrl());
435         $this->withHtml($resp)->assertElementNotContains('.sidebar-page-list', $page->name);
436     }
437
438     public function test_restricted_pages_not_visible_in_book_navigation_on_chapters()
439     {
440         $chapter = $this->entities->chapter();
441         $page = $chapter->pages->first();
442
443         $this->setRestrictionsForTestRoles($page, []);
444
445         $resp = $this->actingAs($this->user)->get($chapter->getUrl());
446         $this->withHtml($resp)->assertElementNotContains('.sidebar-page-list', $page->name);
447     }
448
449     public function test_restricted_pages_not_visible_on_chapter_pages()
450     {
451         $chapter = $this->entities->chapter();
452         $page = $chapter->pages->first();
453
454         $this->setRestrictionsForTestRoles($page, []);
455
456         $this->actingAs($this->user)
457             ->get($chapter->getUrl())
458             ->assertDontSee($page->name);
459     }
460
461     public function test_restricted_chapter_pages_not_visible_on_book_page()
462     {
463         $chapter = $this->entities->chapter();
464         $this->actingAs($this->user)
465             ->get($chapter->book->getUrl())
466             ->assertSee($chapter->pages->first()->name);
467
468         foreach ($chapter->pages as $page) {
469             $this->setRestrictionsForTestRoles($page, []);
470         }
471
472         $this->actingAs($this->user)
473             ->get($chapter->book->getUrl())
474             ->assertDontSee($chapter->pages->first()->name);
475     }
476
477     public function test_bookshelf_update_restriction_override()
478     {
479         $shelf = $this->entities->shelf();
480
481         $this->actingAs($this->viewer)
482             ->get($shelf->getUrl('/edit'))
483             ->assertDontSee('Edit Book');
484
485         $this->setRestrictionsForTestRoles($shelf, ['view', 'delete']);
486
487         $this->get($shelf->getUrl('/edit'))->assertRedirect('/');
488         $this->get('/')->assertSee('You do not have permission');
489
490         $this->setRestrictionsForTestRoles($shelf, ['view', 'update']);
491
492         $this->get($shelf->getUrl('/edit'))->assertOk();
493     }
494
495     public function test_bookshelf_delete_restriction_override()
496     {
497         $shelf = $this->entities->shelf();
498
499         $this->actingAs($this->viewer)
500             ->get($shelf->getUrl('/delete'))
501             ->assertDontSee('Delete Book');
502
503         $this->setRestrictionsForTestRoles($shelf, ['view', 'update']);
504
505         $this->get($shelf->getUrl('/delete'))->assertRedirect('/');
506         $this->get('/')->assertSee('You do not have permission');
507
508         $this->setRestrictionsForTestRoles($shelf, ['view', 'delete']);
509
510         $this->get($shelf->getUrl('/delete'))->assertOk()->assertSee('Delete Shelf');
511     }
512
513     public function test_book_create_restriction_override()
514     {
515         $book = $this->entities->book();
516
517         $bookUrl = $book->getUrl();
518         $resp = $this->actingAs($this->viewer)->get($bookUrl);
519         $this->withHtml($resp)->assertElementNotContains('.actions', 'New Page')
520             ->assertElementNotContains('.actions', 'New Chapter');
521
522         $this->setRestrictionsForTestRoles($book, ['view', 'delete', 'update']);
523
524         $this->get($bookUrl . '/create-chapter')->assertRedirect('/');
525         $this->get('/')->assertSee('You do not have permission');
526         $this->get($bookUrl . '/create-page')->assertRedirect('/');
527         $this->get('/')->assertSee('You do not have permission');
528         $resp = $this->get($bookUrl);
529         $this->withHtml($resp)->assertElementNotContains('.actions', 'New Page')
530             ->assertElementNotContains('.actions', 'New Chapter');
531
532         $this->setRestrictionsForTestRoles($book, ['view', 'create']);
533
534         $resp = $this->post($book->getUrl('/create-chapter'), [
535             'name'        => 'test chapter',
536             'description' => 'test desc',
537         ]);
538         $resp->assertRedirect($book->getUrl('/chapter/test-chapter'));
539
540         $this->get($book->getUrl('/create-page'));
541         /** @var Page $page */
542         $page = Page::query()->where('draft', '=', true)->orderByDesc('id')->first();
543         $resp = $this->post($page->getUrl(), [
544             'name' => 'test page',
545             'html' => 'test desc',
546         ]);
547         $resp->assertRedirect($book->getUrl('/page/test-page'));
548
549         $resp = $this->get($bookUrl);
550         $this->withHtml($resp)->assertElementContains('.actions', 'New Page')
551             ->assertElementContains('.actions', 'New Chapter');
552     }
553
554     public function test_book_update_restriction_override()
555     {
556         $book = $this->entities->book();
557         $bookPage = $book->pages->first();
558         $bookChapter = $book->chapters->first();
559
560         $bookUrl = $book->getUrl();
561         $this->actingAs($this->viewer)->get($bookUrl . '/edit')
562             ->assertDontSee('Edit Book');
563
564         $this->setRestrictionsForTestRoles($book, ['view', 'delete']);
565
566         $this->get($bookUrl . '/edit')->assertRedirect('/');
567         $this->get('/')->assertSee('You do not have permission');
568         $this->get($bookPage->getUrl() . '/edit')->assertRedirect('/');
569         $this->get('/')->assertSee('You do not have permission');
570         $this->get($bookChapter->getUrl() . '/edit')->assertRedirect('/');
571         $this->get('/')->assertSee('You do not have permission');
572
573         $this->setRestrictionsForTestRoles($book, ['view', 'update']);
574
575         $this->get($bookUrl . '/edit')->assertOk();
576         $this->get($bookPage->getUrl() . '/edit')->assertOk();
577         $this->get($bookChapter->getUrl() . '/edit')->assertSee('Edit Chapter');
578     }
579
580     public function test_book_delete_restriction_override()
581     {
582         $book = $this->entities->book();
583         $bookPage = $book->pages->first();
584         $bookChapter = $book->chapters->first();
585
586         $bookUrl = $book->getUrl();
587         $this->actingAs($this->viewer)
588             ->get($bookUrl . '/delete')
589             ->assertDontSee('Delete Book');
590
591         $this->setRestrictionsForTestRoles($book, ['view', 'update']);
592
593         $this->get($bookUrl . '/delete')->assertRedirect('/');
594         $this->get('/')->assertSee('You do not have permission');
595         $this->get($bookPage->getUrl() . '/delete')->assertRedirect('/');
596         $this->get('/')->assertSee('You do not have permission');
597         $this->get($bookChapter->getUrl() . '/delete')->assertRedirect('/');
598         $this->get('/')->assertSee('You do not have permission');
599
600         $this->setRestrictionsForTestRoles($book, ['view', 'delete']);
601
602         $this->get($bookUrl . '/delete')->assertOk()->assertSee('Delete Book');
603         $this->get($bookPage->getUrl() . '/delete')->assertOk()->assertSee('Delete Page');
604         $this->get($bookChapter->getUrl() . '/delete')->assertSee('Delete Chapter');
605     }
606
607     public function test_page_visible_if_has_permissions_when_book_not_visible()
608     {
609         $book = $this->entities->book();
610         $bookChapter = $book->chapters->first();
611         $bookPage = $bookChapter->pages->first();
612
613         foreach ([$book, $bookChapter, $bookPage] as $entity) {
614             $entity->name = Str::random(24);
615             $entity->save();
616         }
617
618         $this->setRestrictionsForTestRoles($book, []);
619         $this->setRestrictionsForTestRoles($bookPage, ['view']);
620
621         $this->actingAs($this->viewer);
622         $resp = $this->get($bookPage->getUrl());
623         $resp->assertOk();
624         $resp->assertSee($bookPage->name);
625         $resp->assertDontSee(substr($book->name, 0, 15));
626         $resp->assertDontSee(substr($bookChapter->name, 0, 15));
627     }
628
629     public function test_book_sort_view_permission()
630     {
631         /** @var Book $firstBook */
632         $firstBook = Book::query()->first();
633         /** @var Book $secondBook */
634         $secondBook = Book::query()->find(2);
635
636         $this->setRestrictionsForTestRoles($firstBook, ['view', 'update']);
637         $this->setRestrictionsForTestRoles($secondBook, ['view']);
638
639         // Test sort page visibility
640         $this->actingAs($this->user)->get($secondBook->getUrl('/sort'))->assertRedirect('/');
641         $this->get('/')->assertSee('You do not have permission');
642
643         // Check sort page on first book
644         $this->actingAs($this->user)->get($firstBook->getUrl('/sort'));
645     }
646
647     public function test_can_create_page_if_chapter_has_permissions_when_book_not_visible()
648     {
649         $book = $this->entities->book();
650         $this->setRestrictionsForTestRoles($book, []);
651         $bookChapter = $book->chapters->first();
652         $this->setRestrictionsForTestRoles($bookChapter, ['view']);
653
654         $this->actingAs($this->user)->get($bookChapter->getUrl())
655             ->assertDontSee('New Page');
656
657         $this->setRestrictionsForTestRoles($bookChapter, ['view', 'create']);
658
659         $this->get($bookChapter->getUrl('/create-page'));
660         /** @var Page $page */
661         $page = Page::query()->where('draft', '=', true)->orderByDesc('id')->first();
662         $resp = $this->post($page->getUrl(), [
663             'name' => 'test page',
664             'html' => 'test content',
665         ]);
666         $resp->assertRedirect($book->getUrl('/page/test-page'));
667     }
668
669     public function test_access_to_item_prevented_if_inheritance_active_but_permission_prevented_via_role()
670     {
671         $user = $this->users->viewer();
672         $viewerRole = $user->roles->first();
673         $chapter = $this->entities->chapter();
674         $book = $chapter->book;
675
676         $this->permissions->setEntityPermissions($book, ['update'], [$viewerRole], false);
677         $this->permissions->setEntityPermissions($chapter, [], [$viewerRole], true);
678
679         $this->assertFalse(userCan('chapter-update', $chapter));
680     }
681
682     public function test_access_to_item_allowed_if_inheritance_active_and_permission_prevented_via_role_but_allowed_via_parent()
683     {
684         $user = $this->users->viewer();
685         $viewerRole = $user->roles->first();
686         $editorRole = Role::getRole('Editor');
687         $user->attachRole($editorRole);
688         $chapter = $this->entities->chapter();
689         $book = $chapter->book;
690
691         $this->permissions->setEntityPermissions($book, ['update'], [$editorRole], false);
692         $this->permissions->setEntityPermissions($chapter, [], [$viewerRole], true);
693
694         $this->actingAs($user);
695         $this->assertTrue(userCan('chapter-update', $chapter));
696     }
697
698     public function test_book_permissions_can_be_generated_without_error_if_child_chapter_is_in_recycle_bin()
699     {
700         $book = $this->entities->bookHasChaptersAndPages();
701         /** @var Chapter $chapter */
702         $chapter = $book->chapters()->first();
703
704         $this->asAdmin()->delete($chapter->getUrl());
705
706         $error = null;
707         try {
708             $this->permissions->setEntityPermissions($book, ['view'], []);
709         } catch (Exception $e) {
710             $error = $e;
711         }
712
713         $this->assertNull($error);
714     }
715 }