]> BookStack Code Mirror - bookstack/blob - tests/Permissions/RolePermissionsTest.php
Merge pull request #4615 from BookStackApp/user_account
[bookstack] / tests / Permissions / RolePermissionsTest.php
1 <?php
2
3 namespace Tests\Permissions;
4
5 use BookStack\Activity\ActivityType;
6 use BookStack\Activity\Models\Comment;
7 use BookStack\Entities\Models\Book;
8 use BookStack\Entities\Models\Bookshelf;
9 use BookStack\Entities\Models\Chapter;
10 use BookStack\Entities\Models\Entity;
11 use BookStack\Entities\Models\Page;
12 use BookStack\Uploads\Image;
13 use BookStack\Users\Models\Role;
14 use BookStack\Users\Models\User;
15 use Illuminate\Testing\TestResponse;
16 use Tests\TestCase;
17
18 class RolePermissionsTest extends TestCase
19 {
20     protected User $user;
21
22     protected function setUp(): void
23     {
24         parent::setUp();
25         $this->user = $this->users->viewer();
26     }
27
28     public function test_manage_user_permission()
29     {
30         $this->actingAs($this->user)->get('/settings/users')->assertRedirect('/');
31         $this->permissions->grantUserRolePermissions($this->user, ['users-manage']);
32         $this->actingAs($this->user)->get('/settings/users')->assertOk();
33     }
34
35     public function test_manage_users_permission_shows_link_in_header_if_does_not_have_settings_manage_permision()
36     {
37         $usersLink = 'href="' . url('/settings/users') . '"';
38         $this->actingAs($this->user)->get('/')->assertDontSee($usersLink, false);
39         $this->permissions->grantUserRolePermissions($this->user, ['users-manage']);
40         $this->actingAs($this->user)->get('/')->assertSee($usersLink, false);
41         $this->permissions->grantUserRolePermissions($this->user, ['settings-manage', 'users-manage']);
42         $this->actingAs($this->user)->get('/')->assertDontSee($usersLink, false);
43     }
44
45     public function test_user_cannot_change_email_unless_they_have_manage_users_permission()
46     {
47         $originalEmail = $this->user->email;
48         $this->actingAs($this->user);
49
50         $resp = $this->get('/my-account/profile')->assertOk();
51         $this->withHtml($resp)->assertElementExists('input[name=email][disabled]');
52         $resp->assertSee('Unfortunately you don\'t have permission to change your email address.');
53         $this->put('/my-account/profile', [
54             'name'  => 'my_new_name',
55             'email' => '[email protected]',
56         ]);
57         $this->assertDatabaseHas('users', [
58             'id'    => $this->user->id,
59             'email' => $originalEmail,
60             'name'  => 'my_new_name',
61         ]);
62
63         $this->permissions->grantUserRolePermissions($this->user, ['users-manage']);
64
65         $resp = $this->get('/my-account/profile')->assertOk();
66         $this->withHtml($resp)
67             ->assertElementNotExists('input[name=email][disabled]')
68             ->assertElementExists('input[name=email]');
69
70         $this->put('/my-account/profile', [
71             'name'  => 'my_new_name_2',
72             'email' => '[email protected]',
73         ]);
74
75         $this->assertDatabaseHas('users', [
76             'id'    => $this->user->id,
77             'email' => '[email protected]',
78             'name'  => 'my_new_name_2',
79         ]);
80     }
81
82     public function test_user_roles_manage_permission()
83     {
84         $this->actingAs($this->user)->get('/settings/roles')->assertRedirect('/');
85         $this->get('/settings/roles/1')->assertRedirect('/');
86         $this->permissions->grantUserRolePermissions($this->user, ['user-roles-manage']);
87         $this->actingAs($this->user)->get('/settings/roles')->assertOk();
88         $this->get('/settings/roles/1')
89             ->assertOk()
90             ->assertSee('Admin');
91     }
92
93     public function test_settings_manage_permission()
94     {
95         $this->actingAs($this->user)->get('/settings/features')->assertRedirect('/');
96         $this->permissions->grantUserRolePermissions($this->user, ['settings-manage']);
97         $this->get('/settings/features')->assertOk();
98
99         $resp = $this->post('/settings/features', []);
100         $resp->assertRedirect('/settings/features');
101         $resp = $this->get('/settings/features');
102         $resp->assertSee('Settings successfully updated');
103     }
104
105     public function test_restrictions_manage_all_permission()
106     {
107         $page = $this->entities->page();
108
109         $this->actingAs($this->user)->get($page->getUrl())->assertDontSee('Permissions');
110         $this->get($page->getUrl('/permissions'))->assertRedirect('/');
111
112         $this->permissions->grantUserRolePermissions($this->user, ['restrictions-manage-all']);
113
114         $this->actingAs($this->user)->get($page->getUrl())->assertSee('Permissions');
115
116         $this->get($page->getUrl('/permissions'))
117             ->assertOk()
118             ->assertSee('Page Permissions');
119     }
120
121     public function test_restrictions_manage_own_permission()
122     {
123         $otherUsersPage = $this->entities->page();
124         $content = $this->entities->createChainBelongingToUser($this->user);
125
126         // Set a different creator on the page we're checking to ensure
127         // that the owner fields are checked
128         $page = $content['page']; /** @var Page $page */
129         $page->created_by = $otherUsersPage->id;
130         $page->owned_by = $this->user->id;
131         $page->save();
132
133         // Check can't restrict other's content
134         $this->actingAs($this->user)->get($otherUsersPage->getUrl())->assertDontSee('Permissions');
135         $this->get($otherUsersPage->getUrl('/permissions'))->assertRedirect('/');
136
137         // Check can't restrict own content
138         $this->actingAs($this->user)->get($page->getUrl())->assertDontSee('Permissions');
139         $this->get($page->getUrl('/permissions'))->assertRedirect('/');
140
141         $this->permissions->grantUserRolePermissions($this->user, ['restrictions-manage-own']);
142
143         // Check can't restrict other's content
144         $this->actingAs($this->user)->get($otherUsersPage->getUrl())->assertDontSee('Permissions');
145         $this->get($otherUsersPage->getUrl('/permissions'))->assertRedirect();
146
147         // Check can restrict own content
148         $this->actingAs($this->user)->get($page->getUrl())->assertSee('Permissions');
149         $this->get($page->getUrl('/permissions'))->assertOk();
150     }
151
152     /**
153      * Check a standard entity access permission.
154      */
155     private function checkAccessPermission(string $permission, array $accessUrls = [], array $visibles = [])
156     {
157         foreach ($accessUrls as $url) {
158             $this->actingAs($this->user)->get($url)->assertRedirect('/');
159         }
160
161         foreach ($visibles as $url => $text) {
162             $resp = $this->actingAs($this->user)->get($url);
163             $this->withHtml($resp)->assertElementNotContains('.action-buttons', $text);
164         }
165
166         $this->permissions->grantUserRolePermissions($this->user, [$permission]);
167
168         foreach ($accessUrls as $url) {
169             $this->actingAs($this->user)->get($url)->assertOk();
170         }
171         foreach ($visibles as $url => $text) {
172             $this->actingAs($this->user)->get($url)->assertSee($text);
173         }
174     }
175
176     public function test_bookshelves_create_all_permissions()
177     {
178         $this->checkAccessPermission('bookshelf-create-all', [
179             '/create-shelf',
180         ], [
181             '/shelves' => 'New Shelf',
182         ]);
183
184         $this->post('/shelves', [
185             'name'        => 'test shelf',
186             'description' => 'shelf desc',
187         ])->assertRedirect('/shelves/test-shelf');
188     }
189
190     public function test_bookshelves_edit_own_permission()
191     {
192         /** @var Bookshelf $otherShelf */
193         $otherShelf = Bookshelf::query()->first();
194         $ownShelf = $this->entities->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
195         $ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
196         $this->permissions->regenerateForEntity($ownShelf);
197
198         $this->checkAccessPermission('bookshelf-update-own', [
199             $ownShelf->getUrl('/edit'),
200         ], [
201             $ownShelf->getUrl() => 'Edit',
202         ]);
203
204         $resp = $this->get($otherShelf->getUrl());
205         $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'Edit');
206         $this->get($otherShelf->getUrl('/edit'))->assertRedirect('/');
207     }
208
209     public function test_bookshelves_edit_all_permission()
210     {
211         /** @var Bookshelf $otherShelf */
212         $otherShelf = Bookshelf::query()->first();
213         $this->checkAccessPermission('bookshelf-update-all', [
214             $otherShelf->getUrl('/edit'),
215         ], [
216             $otherShelf->getUrl() => 'Edit',
217         ]);
218     }
219
220     public function test_bookshelves_delete_own_permission()
221     {
222         $this->permissions->grantUserRolePermissions($this->user, ['bookshelf-update-all']);
223         /** @var Bookshelf $otherShelf */
224         $otherShelf = Bookshelf::query()->first();
225         $ownShelf = $this->entities->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
226         $ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
227         $this->permissions->regenerateForEntity($ownShelf);
228
229         $this->checkAccessPermission('bookshelf-delete-own', [
230             $ownShelf->getUrl('/delete'),
231         ], [
232             $ownShelf->getUrl() => 'Delete',
233         ]);
234
235         $resp = $this->get($otherShelf->getUrl());
236         $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'Delete');
237         $this->get($otherShelf->getUrl('/delete'))->assertRedirect('/');
238
239         $this->get($ownShelf->getUrl());
240         $this->delete($ownShelf->getUrl())->assertRedirect('/shelves');
241         $this->get('/shelves')->assertDontSee($ownShelf->name);
242     }
243
244     public function test_bookshelves_delete_all_permission()
245     {
246         $this->permissions->grantUserRolePermissions($this->user, ['bookshelf-update-all']);
247         /** @var Bookshelf $otherShelf */
248         $otherShelf = Bookshelf::query()->first();
249         $this->checkAccessPermission('bookshelf-delete-all', [
250             $otherShelf->getUrl('/delete'),
251         ], [
252             $otherShelf->getUrl() => 'Delete',
253         ]);
254
255         $this->delete($otherShelf->getUrl())->assertRedirect('/shelves');
256         $this->get('/shelves')->assertDontSee($otherShelf->name);
257     }
258
259     public function test_books_create_all_permissions()
260     {
261         $this->checkAccessPermission('book-create-all', [
262             '/create-book',
263         ], [
264             '/books' => 'Create New Book',
265         ]);
266
267         $this->post('/books', [
268             'name'        => 'test book',
269             'description' => 'book desc',
270         ])->assertRedirect('/books/test-book');
271     }
272
273     public function test_books_edit_own_permission()
274     {
275         /** @var Book $otherBook */
276         $otherBook = Book::query()->take(1)->get()->first();
277         $ownBook = $this->entities->createChainBelongingToUser($this->user)['book'];
278         $this->checkAccessPermission('book-update-own', [
279             $ownBook->getUrl() . '/edit',
280         ], [
281             $ownBook->getUrl() => 'Edit',
282         ]);
283
284         $resp = $this->get($otherBook->getUrl());
285         $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'Edit');
286         $this->get($otherBook->getUrl('/edit'))->assertRedirect('/');
287     }
288
289     public function test_books_edit_all_permission()
290     {
291         /** @var Book $otherBook */
292         $otherBook = Book::query()->take(1)->get()->first();
293         $this->checkAccessPermission('book-update-all', [
294             $otherBook->getUrl() . '/edit',
295         ], [
296             $otherBook->getUrl() => 'Edit',
297         ]);
298     }
299
300     public function test_books_delete_own_permission()
301     {
302         $this->permissions->grantUserRolePermissions($this->user, ['book-update-all']);
303         /** @var Book $otherBook */
304         $otherBook = Book::query()->take(1)->get()->first();
305         $ownBook = $this->entities->createChainBelongingToUser($this->user)['book'];
306         $this->checkAccessPermission('book-delete-own', [
307             $ownBook->getUrl() . '/delete',
308         ], [
309             $ownBook->getUrl() => 'Delete',
310         ]);
311
312         $resp = $this->get($otherBook->getUrl());
313         $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'Delete');
314         $this->get($otherBook->getUrl('/delete'))->assertRedirect('/');
315         $this->get($ownBook->getUrl());
316         $this->delete($ownBook->getUrl())->assertRedirect('/books');
317         $this->get('/books')->assertDontSee($ownBook->name);
318     }
319
320     public function test_books_delete_all_permission()
321     {
322         $this->permissions->grantUserRolePermissions($this->user, ['book-update-all']);
323         /** @var Book $otherBook */
324         $otherBook = Book::query()->take(1)->get()->first();
325         $this->checkAccessPermission('book-delete-all', [
326             $otherBook->getUrl() . '/delete',
327         ], [
328             $otherBook->getUrl() => 'Delete',
329         ]);
330
331         $this->get($otherBook->getUrl());
332         $this->delete($otherBook->getUrl())->assertRedirect('/books');
333         $this->get('/books')->assertDontSee($otherBook->name);
334     }
335
336     public function test_chapter_create_own_permissions()
337     {
338         /** @var Book $book */
339         $book = Book::query()->take(1)->get()->first();
340         $ownBook = $this->entities->createChainBelongingToUser($this->user)['book'];
341         $this->checkAccessPermission('chapter-create-own', [
342             $ownBook->getUrl('/create-chapter'),
343         ], [
344             $ownBook->getUrl() => 'New Chapter',
345         ]);
346
347         $this->post($ownBook->getUrl('/create-chapter'), [
348             'name'        => 'test chapter',
349             'description' => 'chapter desc',
350         ])->assertRedirect($ownBook->getUrl('/chapter/test-chapter'));
351
352         $resp = $this->get($book->getUrl());
353         $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'New Chapter');
354         $this->get($book->getUrl('/create-chapter'))->assertRedirect('/');
355     }
356
357     public function test_chapter_create_all_permissions()
358     {
359         $book = $this->entities->book();
360         $this->checkAccessPermission('chapter-create-all', [
361             $book->getUrl('/create-chapter'),
362         ], [
363             $book->getUrl() => 'New Chapter',
364         ]);
365
366         $this->post($book->getUrl('/create-chapter'), [
367             'name'        => 'test chapter',
368             'description' => 'chapter desc',
369         ])->assertRedirect($book->getUrl('/chapter/test-chapter'));
370     }
371
372     public function test_chapter_edit_own_permission()
373     {
374         /** @var Chapter $otherChapter */
375         $otherChapter = Chapter::query()->first();
376         $ownChapter = $this->entities->createChainBelongingToUser($this->user)['chapter'];
377         $this->checkAccessPermission('chapter-update-own', [
378             $ownChapter->getUrl() . '/edit',
379         ], [
380             $ownChapter->getUrl() => 'Edit',
381         ]);
382
383         $resp = $this->get($otherChapter->getUrl());
384         $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'Edit');
385         $this->get($otherChapter->getUrl('/edit'))->assertRedirect('/');
386     }
387
388     public function test_chapter_edit_all_permission()
389     {
390         /** @var Chapter $otherChapter */
391         $otherChapter = Chapter::query()->take(1)->get()->first();
392         $this->checkAccessPermission('chapter-update-all', [
393             $otherChapter->getUrl() . '/edit',
394         ], [
395             $otherChapter->getUrl() => 'Edit',
396         ]);
397     }
398
399     public function test_chapter_delete_own_permission()
400     {
401         $this->permissions->grantUserRolePermissions($this->user, ['chapter-update-all']);
402         /** @var Chapter $otherChapter */
403         $otherChapter = Chapter::query()->first();
404         $ownChapter = $this->entities->createChainBelongingToUser($this->user)['chapter'];
405         $this->checkAccessPermission('chapter-delete-own', [
406             $ownChapter->getUrl() . '/delete',
407         ], [
408             $ownChapter->getUrl() => 'Delete',
409         ]);
410
411         $bookUrl = $ownChapter->book->getUrl();
412         $resp = $this->get($otherChapter->getUrl());
413         $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'Delete');
414         $this->get($otherChapter->getUrl('/delete'))->assertRedirect('/');
415         $this->get($ownChapter->getUrl());
416         $this->delete($ownChapter->getUrl())->assertRedirect($bookUrl);
417         $resp = $this->get($bookUrl);
418         $this->withHtml($resp)->assertElementNotContains('.book-content', $ownChapter->name);
419     }
420
421     public function test_chapter_delete_all_permission()
422     {
423         $this->permissions->grantUserRolePermissions($this->user, ['chapter-update-all']);
424         /** @var Chapter $otherChapter */
425         $otherChapter = Chapter::query()->first();
426         $this->checkAccessPermission('chapter-delete-all', [
427             $otherChapter->getUrl() . '/delete',
428         ], [
429             $otherChapter->getUrl() => 'Delete',
430         ]);
431
432         $bookUrl = $otherChapter->book->getUrl();
433         $this->get($otherChapter->getUrl());
434         $this->delete($otherChapter->getUrl())->assertRedirect($bookUrl);
435         $resp = $this->get($bookUrl);
436         $this->withHtml($resp)->assertElementNotContains('.book-content', $otherChapter->name);
437     }
438
439     public function test_page_create_own_permissions()
440     {
441         $book = $this->entities->book();
442         $chapter = $this->entities->chapter();
443
444         $entities = $this->entities->createChainBelongingToUser($this->user);
445         $ownBook = $entities['book'];
446         $ownChapter = $entities['chapter'];
447
448         $createUrl = $ownBook->getUrl('/create-page');
449         $createUrlChapter = $ownChapter->getUrl('/create-page');
450         $accessUrls = [$createUrl, $createUrlChapter];
451
452         foreach ($accessUrls as $url) {
453             $this->actingAs($this->user)->get($url)->assertRedirect('/');
454         }
455
456         $this->checkAccessPermission('page-create-own', [], [
457             $ownBook->getUrl()    => 'New Page',
458             $ownChapter->getUrl() => 'New Page',
459         ]);
460
461         $this->permissions->grantUserRolePermissions($this->user, ['page-create-own']);
462
463         foreach ($accessUrls as $index => $url) {
464             $resp = $this->actingAs($this->user)->get($url);
465             $expectedUrl = Page::query()->where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
466             $resp->assertRedirect($expectedUrl);
467         }
468
469         $this->get($createUrl);
470         /** @var Page $draft */
471         $draft = Page::query()->where('draft', '=', true)->orderBy('id', 'desc')->first();
472         $this->post($draft->getUrl(), [
473             'name' => 'test page',
474             'html' => 'page desc',
475         ])->assertRedirect($ownBook->getUrl('/page/test-page'));
476
477         $resp = $this->get($book->getUrl());
478         $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'New Page');
479         $this->get($book->getUrl('/create-page'))->assertRedirect('/');
480
481         $resp = $this->get($chapter->getUrl());
482         $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'New Page');
483         $this->get($chapter->getUrl('/create-page'))->assertRedirect('/');
484     }
485
486     public function test_page_create_all_permissions()
487     {
488         $book = $this->entities->book();
489         $chapter = $this->entities->chapter();
490         $createUrl = $book->getUrl('/create-page');
491
492         $createUrlChapter = $chapter->getUrl('/create-page');
493         $accessUrls = [$createUrl, $createUrlChapter];
494
495         foreach ($accessUrls as $url) {
496             $this->actingAs($this->user)->get($url)->assertRedirect('/');
497         }
498
499         $this->checkAccessPermission('page-create-all', [], [
500             $book->getUrl()    => 'New Page',
501             $chapter->getUrl() => 'New Page',
502         ]);
503
504         $this->permissions->grantUserRolePermissions($this->user, ['page-create-all']);
505
506         foreach ($accessUrls as $index => $url) {
507             $resp = $this->actingAs($this->user)->get($url);
508             $expectedUrl = Page::query()->where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
509             $resp->assertRedirect($expectedUrl);
510         }
511
512         $this->get($createUrl);
513         /** @var Page $draft */
514         $draft = Page::query()->where('draft', '=', true)->orderByDesc('id')->first();
515         $this->post($draft->getUrl(), [
516             'name' => 'test page',
517             'html' => 'page desc',
518         ])->assertRedirect($book->getUrl('/page/test-page'));
519
520         $this->get($chapter->getUrl('/create-page'));
521         /** @var Page $draft */
522         $draft = Page::query()->where('draft', '=', true)->orderByDesc('id')->first();
523         $this->post($draft->getUrl(), [
524             'name' => 'new test page',
525             'html' => 'page desc',
526         ])->assertRedirect($book->getUrl('/page/new-test-page'));
527     }
528
529     public function test_page_edit_own_permission()
530     {
531         /** @var Page $otherPage */
532         $otherPage = Page::query()->first();
533         $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
534         $this->checkAccessPermission('page-update-own', [
535             $ownPage->getUrl() . '/edit',
536         ], [
537             $ownPage->getUrl() => 'Edit',
538         ]);
539
540         $resp = $this->get($otherPage->getUrl());
541         $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'Edit');
542         $this->get($otherPage->getUrl() . '/edit')->assertRedirect('/');
543     }
544
545     public function test_page_edit_all_permission()
546     {
547         /** @var Page $otherPage */
548         $otherPage = Page::query()->first();
549         $this->checkAccessPermission('page-update-all', [
550             $otherPage->getUrl('/edit'),
551         ], [
552             $otherPage->getUrl() => 'Edit',
553         ]);
554     }
555
556     public function test_page_delete_own_permission()
557     {
558         $this->permissions->grantUserRolePermissions($this->user, ['page-update-all']);
559         /** @var Page $otherPage */
560         $otherPage = Page::query()->first();
561         $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
562         $this->checkAccessPermission('page-delete-own', [
563             $ownPage->getUrl() . '/delete',
564         ], [
565             $ownPage->getUrl() => 'Delete',
566         ]);
567
568         $parent = $ownPage->chapter ?? $ownPage->book;
569         $resp = $this->get($otherPage->getUrl());
570         $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'Delete');
571         $this->get($otherPage->getUrl('/delete'))->assertRedirect('/');
572         $this->get($ownPage->getUrl());
573         $this->delete($ownPage->getUrl())->assertRedirect($parent->getUrl());
574         $resp = $this->get($parent->getUrl());
575         $this->withHtml($resp)->assertElementNotContains('.book-content', $ownPage->name);
576     }
577
578     public function test_page_delete_all_permission()
579     {
580         $this->permissions->grantUserRolePermissions($this->user, ['page-update-all']);
581         /** @var Page $otherPage */
582         $otherPage = Page::query()->first();
583
584         $this->checkAccessPermission('page-delete-all', [
585             $otherPage->getUrl() . '/delete',
586         ], [
587             $otherPage->getUrl() => 'Delete',
588         ]);
589
590         /** @var Entity $parent */
591         $parent = $otherPage->chapter ?? $otherPage->book;
592         $this->get($otherPage->getUrl());
593
594         $this->delete($otherPage->getUrl())->assertRedirect($parent->getUrl());
595         $this->get($parent->getUrl())->assertDontSee($otherPage->name);
596     }
597
598
599     public function test_image_delete_own_permission()
600     {
601         $this->permissions->grantUserRolePermissions($this->user, ['image-update-all']);
602         $page = $this->entities->page();
603         $image = Image::factory()->create([
604             'uploaded_to' => $page->id,
605             'created_by'  => $this->user->id,
606             'updated_by'  => $this->user->id,
607         ]);
608
609         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)->assertStatus(403);
610
611         $this->permissions->grantUserRolePermissions($this->user, ['image-delete-own']);
612
613         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)->assertOk();
614         $this->assertDatabaseMissing('images', ['id' => $image->id]);
615     }
616
617     public function test_image_delete_all_permission()
618     {
619         $this->permissions->grantUserRolePermissions($this->user, ['image-update-all']);
620         $admin = $this->users->admin();
621         $page = $this->entities->page();
622         $image = Image::factory()->create(['uploaded_to' => $page->id, 'created_by' => $admin->id, 'updated_by' => $admin->id]);
623
624         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)->assertStatus(403);
625
626         $this->permissions->grantUserRolePermissions($this->user, ['image-delete-own']);
627
628         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)->assertStatus(403);
629
630         $this->permissions->grantUserRolePermissions($this->user, ['image-delete-all']);
631
632         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)->assertOk();
633         $this->assertDatabaseMissing('images', ['id' => $image->id]);
634     }
635
636     public function test_empty_state_actions_not_visible_without_permission()
637     {
638         $admin = $this->users->admin();
639         // Book links
640         $book = Book::factory()->create(['created_by' => $admin->id, 'updated_by' => $admin->id]);
641         $this->permissions->regenerateForEntity($book);
642         $this->actingAs($this->users->viewer())->get($book->getUrl())
643             ->assertDontSee('Create a new page')
644             ->assertDontSee('Add a chapter');
645
646         // Chapter links
647         $chapter = Chapter::factory()->create(['created_by' => $admin->id, 'updated_by' => $admin->id, 'book_id' => $book->id]);
648         $this->permissions->regenerateForEntity($chapter);
649         $this->actingAs($this->users->viewer())->get($chapter->getUrl())
650             ->assertDontSee('Create a new page')
651             ->assertDontSee('Sort the current book');
652     }
653
654     public function test_comment_create_permission()
655     {
656         $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
657
658         $this->actingAs($this->user)
659             ->addComment($ownPage)
660             ->assertStatus(403);
661
662         $this->permissions->grantUserRolePermissions($this->user, ['comment-create-all']);
663
664         $this->actingAs($this->user)
665             ->addComment($ownPage)
666             ->assertOk();
667     }
668
669     public function test_comment_update_own_permission()
670     {
671         $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
672         $this->permissions->grantUserRolePermissions($this->user, ['comment-create-all']);
673         $this->actingAs($this->user)->addComment($ownPage);
674         /** @var Comment $comment */
675         $comment = $ownPage->comments()->latest()->first();
676
677         // no comment-update-own
678         $this->actingAs($this->user)->updateComment($comment)->assertStatus(403);
679
680         $this->permissions->grantUserRolePermissions($this->user, ['comment-update-own']);
681
682         // now has comment-update-own
683         $this->actingAs($this->user)->updateComment($comment)->assertOk();
684     }
685
686     public function test_comment_update_all_permission()
687     {
688         /** @var Page $ownPage */
689         $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
690         $this->asAdmin()->addComment($ownPage);
691         /** @var Comment $comment */
692         $comment = $ownPage->comments()->latest()->first();
693
694         // no comment-update-all
695         $this->actingAs($this->user)->updateComment($comment)->assertStatus(403);
696
697         $this->permissions->grantUserRolePermissions($this->user, ['comment-update-all']);
698
699         // now has comment-update-all
700         $this->actingAs($this->user)->updateComment($comment)->assertOk();
701     }
702
703     public function test_comment_delete_own_permission()
704     {
705         /** @var Page $ownPage */
706         $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
707         $this->permissions->grantUserRolePermissions($this->user, ['comment-create-all']);
708         $this->actingAs($this->user)->addComment($ownPage);
709
710         /** @var Comment $comment */
711         $comment = $ownPage->comments()->latest()->first();
712
713         // no comment-delete-own
714         $this->actingAs($this->user)->deleteComment($comment)->assertStatus(403);
715
716         $this->permissions->grantUserRolePermissions($this->user, ['comment-delete-own']);
717
718         // now has comment-update-own
719         $this->actingAs($this->user)->deleteComment($comment)->assertOk();
720     }
721
722     public function test_comment_delete_all_permission()
723     {
724         /** @var Page $ownPage */
725         $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
726         $this->asAdmin()->addComment($ownPage);
727         /** @var Comment $comment */
728         $comment = $ownPage->comments()->latest()->first();
729
730         // no comment-delete-all
731         $this->actingAs($this->user)->deleteComment($comment)->assertStatus(403);
732
733         $this->permissions->grantUserRolePermissions($this->user, ['comment-delete-all']);
734
735         // now has comment-delete-all
736         $this->actingAs($this->user)->deleteComment($comment)->assertOk();
737     }
738
739     private function addComment(Page $page): TestResponse
740     {
741         $comment = Comment::factory()->make();
742
743         return $this->postJson("/comment/$page->id", $comment->only('text', 'html'));
744     }
745
746     private function updateComment(Comment $comment): TestResponse
747     {
748         $commentData = Comment::factory()->make();
749
750         return $this->putJson("/comment/{$comment->id}", $commentData->only('text', 'html'));
751     }
752
753     private function deleteComment(Comment $comment): TestResponse
754     {
755         return $this->json('DELETE', '/comment/' . $comment->id);
756     }
757 }