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