]> BookStack Code Mirror - bookstack/blob - tests/Permissions/RestrictionsTest.php
Fixes an issue with handling of large image files.
[bookstack] / tests / Permissions / RestrictionsTest.php
1 <?php namespace Tests;
2
3 use BookStack\Book;
4 use BookStack\Services\PermissionService;
5 use BookStack\User;
6 use BookStack\Repos\EntityRepo;
7
8 class RestrictionsTest extends BrowserKitTest
9 {
10
11     /**
12      * @var User
13      */
14     protected $user;
15
16     /**
17      * @var User
18      */
19     protected $viewer;
20
21     /**
22      * @var PermissionService
23      */
24     protected $permissionService;
25
26     public function setUp()
27     {
28         parent::setUp();
29         $this->user = $this->getEditor();
30         $this->viewer = $this->getViewer();
31         $this->permissionService = $this->app[PermissionService::class];
32     }
33
34     /**
35      * Manually set some permissions on an entity.
36      * @param \BookStack\Entity $entity
37      * @param $actions
38      */
39     protected function setEntityRestrictions(\BookStack\Entity $entity, $actions)
40     {
41         $entity->restricted = true;
42         $entity->permissions()->delete();
43
44         $role = $this->user->roles->first();
45         $viewerRole = $this->viewer->roles->first();
46
47         $permissions = [];
48         foreach ($actions as $action) {
49             $permissions[] = [
50                 'role_id' => $role->id,
51                 'action' => strtolower($action)
52             ];
53             $permissions[] = [
54                 'role_id' => $viewerRole->id,
55                 'action' => strtolower($action)
56             ];
57         }
58         $entity->permissions()->createMany($permissions);
59
60         $entity->save();
61         $entity->load('permissions');
62         $this->permissionService->buildJointPermissionsForEntity($entity);
63         $entity->load('jointPermissions');
64     }
65
66     public function test_book_view_restriction()
67     {
68         $book = Book::first();
69         $bookPage = $book->pages->first();
70         $bookChapter = $book->chapters->first();
71
72         $bookUrl = $book->getUrl();
73         $this->actingAs($this->user)
74             ->visit($bookUrl)
75             ->seePageIs($bookUrl);
76
77         $this->setEntityRestrictions($book, []);
78
79         $this->forceVisit($bookUrl)
80             ->see('Book not found');
81         $this->forceVisit($bookPage->getUrl())
82             ->see('Page not found');
83         $this->forceVisit($bookChapter->getUrl())
84             ->see('Chapter not found');
85
86         $this->setEntityRestrictions($book, ['view']);
87
88         $this->visit($bookUrl)
89             ->see($book->name);
90         $this->visit($bookPage->getUrl())
91             ->see($bookPage->name);
92         $this->visit($bookChapter->getUrl())
93             ->see($bookChapter->name);
94     }
95
96     public function test_book_create_restriction()
97     {
98         $book = Book::first();
99
100         $bookUrl = $book->getUrl();
101         $this->actingAs($this->viewer)
102             ->visit($bookUrl)
103             ->dontSeeInElement('.action-buttons', 'New Page')
104             ->dontSeeInElement('.action-buttons', 'New Chapter');
105         $this->actingAs($this->user)
106             ->visit($bookUrl)
107             ->seeInElement('.action-buttons', 'New Page')
108             ->seeInElement('.action-buttons', 'New Chapter');
109
110         $this->setEntityRestrictions($book, ['view', 'delete', 'update']);
111
112         $this->forceVisit($bookUrl . '/chapter/create')
113             ->see('You do not have permission')->seePageIs('/');
114         $this->forceVisit($bookUrl . '/page/create')
115             ->see('You do not have permission')->seePageIs('/');
116         $this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
117             ->dontSeeInElement('.action-buttons', 'New Chapter');
118
119         $this->setEntityRestrictions($book, ['view', 'create']);
120
121         $this->visit($bookUrl . '/chapter/create')
122             ->type('test chapter', 'name')
123             ->type('test description for chapter', 'description')
124             ->press('Save Chapter')
125             ->seePageIs($bookUrl . '/chapter/test-chapter');
126         $this->visit($bookUrl . '/page/create')
127             ->type('test page', 'name')
128             ->type('test content', 'html')
129             ->press('Save Page')
130             ->seePageIs($bookUrl . '/page/test-page');
131         $this->visit($bookUrl)->seeInElement('.action-buttons', 'New Page')
132             ->seeInElement('.action-buttons', 'New Chapter');
133     }
134
135     public function test_book_update_restriction()
136     {
137         $book = Book::first();
138         $bookPage = $book->pages->first();
139         $bookChapter = $book->chapters->first();
140
141         $bookUrl = $book->getUrl();
142         $this->actingAs($this->user)
143             ->visit($bookUrl . '/edit')
144             ->see('Edit Book');
145
146         $this->setEntityRestrictions($book, ['view', 'delete']);
147
148         $this->forceVisit($bookUrl . '/edit')
149             ->see('You do not have permission')->seePageIs('/');
150         $this->forceVisit($bookPage->getUrl() . '/edit')
151             ->see('You do not have permission')->seePageIs('/');
152         $this->forceVisit($bookChapter->getUrl() . '/edit')
153             ->see('You do not have permission')->seePageIs('/');
154
155         $this->setEntityRestrictions($book, ['view', 'update']);
156
157         $this->visit($bookUrl . '/edit')
158             ->seePageIs($bookUrl . '/edit');
159         $this->visit($bookPage->getUrl() . '/edit')
160             ->seePageIs($bookPage->getUrl() . '/edit');
161         $this->visit($bookChapter->getUrl() . '/edit')
162             ->see('Edit Chapter');
163     }
164
165     public function test_book_delete_restriction()
166     {
167         $book = Book::first();
168         $bookPage = $book->pages->first();
169         $bookChapter = $book->chapters->first();
170
171         $bookUrl = $book->getUrl();
172         $this->actingAs($this->user)
173             ->visit($bookUrl . '/delete')
174             ->see('Delete Book');
175
176         $this->setEntityRestrictions($book, ['view', 'update']);
177
178         $this->forceVisit($bookUrl . '/delete')
179             ->see('You do not have permission')->seePageIs('/');
180         $this->forceVisit($bookPage->getUrl() . '/delete')
181             ->see('You do not have permission')->seePageIs('/');
182         $this->forceVisit($bookChapter->getUrl() . '/delete')
183             ->see('You do not have permission')->seePageIs('/');
184
185         $this->setEntityRestrictions($book, ['view', 'delete']);
186
187         $this->visit($bookUrl . '/delete')
188             ->seePageIs($bookUrl . '/delete')->see('Delete Book');
189         $this->visit($bookPage->getUrl() . '/delete')
190             ->seePageIs($bookPage->getUrl() . '/delete')->see('Delete Page');
191         $this->visit($bookChapter->getUrl() . '/delete')
192             ->see('Delete Chapter');
193     }
194
195     public function test_chapter_view_restriction()
196     {
197         $chapter = \BookStack\Chapter::first();
198         $chapterPage = $chapter->pages->first();
199
200         $chapterUrl = $chapter->getUrl();
201         $this->actingAs($this->user)
202             ->visit($chapterUrl)
203             ->seePageIs($chapterUrl);
204
205         $this->setEntityRestrictions($chapter, []);
206
207         $this->forceVisit($chapterUrl)
208             ->see('Chapter not found');
209         $this->forceVisit($chapterPage->getUrl())
210             ->see('Page not found');
211
212         $this->setEntityRestrictions($chapter, ['view']);
213
214         $this->visit($chapterUrl)
215             ->see($chapter->name);
216         $this->visit($chapterPage->getUrl())
217             ->see($chapterPage->name);
218     }
219
220     public function test_chapter_create_restriction()
221     {
222         $chapter = \BookStack\Chapter::first();
223
224         $chapterUrl = $chapter->getUrl();
225         $this->actingAs($this->user)
226             ->visit($chapterUrl)
227             ->seeInElement('.action-buttons', 'New Page');
228
229         $this->setEntityRestrictions($chapter, ['view', 'delete', 'update']);
230
231         $this->forceVisit($chapterUrl . '/create-page')
232             ->see('You do not have permission')->seePageIs('/');
233         $this->visit($chapterUrl)->dontSeeInElement('.action-buttons', 'New Page');
234
235         $this->setEntityRestrictions($chapter, ['view', 'create']);
236
237
238         $this->visit($chapterUrl . '/create-page')
239             ->type('test page', 'name')
240             ->type('test content', 'html')
241             ->press('Save Page')
242             ->seePageIs($chapter->book->getUrl() . '/page/test-page');
243
244         $this->visit($chapterUrl)->seeInElement('.action-buttons', 'New Page');
245     }
246
247     public function test_chapter_update_restriction()
248     {
249         $chapter = \BookStack\Chapter::first();
250         $chapterPage = $chapter->pages->first();
251
252         $chapterUrl = $chapter->getUrl();
253         $this->actingAs($this->user)
254             ->visit($chapterUrl . '/edit')
255             ->see('Edit Chapter');
256
257         $this->setEntityRestrictions($chapter, ['view', 'delete']);
258
259         $this->forceVisit($chapterUrl . '/edit')
260             ->see('You do not have permission')->seePageIs('/');
261         $this->forceVisit($chapterPage->getUrl() . '/edit')
262             ->see('You do not have permission')->seePageIs('/');
263
264         $this->setEntityRestrictions($chapter, ['view', 'update']);
265
266         $this->visit($chapterUrl . '/edit')
267             ->seePageIs($chapterUrl . '/edit')->see('Edit Chapter');
268         $this->visit($chapterPage->getUrl() . '/edit')
269             ->seePageIs($chapterPage->getUrl() . '/edit');
270     }
271
272     public function test_chapter_delete_restriction()
273     {
274         $chapter = \BookStack\Chapter::first();
275         $chapterPage = $chapter->pages->first();
276
277         $chapterUrl = $chapter->getUrl();
278         $this->actingAs($this->user)
279             ->visit($chapterUrl . '/delete')
280             ->see('Delete Chapter');
281
282         $this->setEntityRestrictions($chapter, ['view', 'update']);
283
284         $this->forceVisit($chapterUrl . '/delete')
285             ->see('You do not have permission')->seePageIs('/');
286         $this->forceVisit($chapterPage->getUrl() . '/delete')
287             ->see('You do not have permission')->seePageIs('/');
288
289         $this->setEntityRestrictions($chapter, ['view', 'delete']);
290
291         $this->visit($chapterUrl . '/delete')
292             ->seePageIs($chapterUrl . '/delete')->see('Delete Chapter');
293         $this->visit($chapterPage->getUrl() . '/delete')
294             ->seePageIs($chapterPage->getUrl() . '/delete')->see('Delete Page');
295     }
296
297     public function test_page_view_restriction()
298     {
299         $page = \BookStack\Page::first();
300
301         $pageUrl = $page->getUrl();
302         $this->actingAs($this->user)
303             ->visit($pageUrl)
304             ->seePageIs($pageUrl);
305
306         $this->setEntityRestrictions($page, ['update', 'delete']);
307
308         $this->forceVisit($pageUrl)
309             ->see('Page not found');
310
311         $this->setEntityRestrictions($page, ['view']);
312
313         $this->visit($pageUrl)
314             ->see($page->name);
315     }
316
317     public function test_page_update_restriction()
318     {
319         $page = \BookStack\Chapter::first();
320
321         $pageUrl = $page->getUrl();
322         $this->actingAs($this->user)
323             ->visit($pageUrl . '/edit')
324             ->seeInField('name', $page->name);
325
326         $this->setEntityRestrictions($page, ['view', 'delete']);
327
328         $this->forceVisit($pageUrl . '/edit')
329             ->see('You do not have permission')->seePageIs('/');
330
331         $this->setEntityRestrictions($page, ['view', 'update']);
332
333         $this->visit($pageUrl . '/edit')
334             ->seePageIs($pageUrl . '/edit')->seeInField('name', $page->name);
335     }
336
337     public function test_page_delete_restriction()
338     {
339         $page = \BookStack\Page::first();
340
341         $pageUrl = $page->getUrl();
342         $this->actingAs($this->user)
343             ->visit($pageUrl . '/delete')
344             ->see('Delete Page');
345
346         $this->setEntityRestrictions($page, ['view', 'update']);
347
348         $this->forceVisit($pageUrl . '/delete')
349             ->see('You do not have permission')->seePageIs('/');
350
351         $this->setEntityRestrictions($page, ['view', 'delete']);
352
353         $this->visit($pageUrl . '/delete')
354             ->seePageIs($pageUrl . '/delete')->see('Delete Page');
355     }
356
357     public function test_book_restriction_form()
358     {
359         $book = Book::first();
360         $this->asAdmin()->visit($book->getUrl() . '/permissions')
361             ->see('Book Permissions')
362             ->check('restricted')
363             ->check('restrictions[2][view]')
364             ->press('Save Permissions')
365             ->seeInDatabase('books', ['id' => $book->id, 'restricted' => true])
366             ->seeInDatabase('entity_permissions', [
367                 'restrictable_id' => $book->id,
368                 'restrictable_type' => 'BookStack\Book',
369                 'role_id' => '2',
370                 'action' => 'view'
371             ]);
372     }
373
374     public function test_chapter_restriction_form()
375     {
376         $chapter = \BookStack\Chapter::first();
377         $this->asAdmin()->visit($chapter->getUrl() . '/permissions')
378             ->see('Chapter Permissions')
379             ->check('restricted')
380             ->check('restrictions[2][update]')
381             ->press('Save Permissions')
382             ->seeInDatabase('chapters', ['id' => $chapter->id, 'restricted' => true])
383             ->seeInDatabase('entity_permissions', [
384                 'restrictable_id' => $chapter->id,
385                 'restrictable_type' => 'BookStack\Chapter',
386                 'role_id' => '2',
387                 'action' => 'update'
388             ]);
389     }
390
391     public function test_page_restriction_form()
392     {
393         $page = \BookStack\Page::first();
394         $this->asAdmin()->visit($page->getUrl() . '/permissions')
395             ->see('Page Permissions')
396             ->check('restricted')
397             ->check('restrictions[2][delete]')
398             ->press('Save Permissions')
399             ->seeInDatabase('pages', ['id' => $page->id, 'restricted' => true])
400             ->seeInDatabase('entity_permissions', [
401                 'restrictable_id' => $page->id,
402                 'restrictable_type' => 'BookStack\Page',
403                 'role_id' => '2',
404                 'action' => 'delete'
405             ]);
406     }
407
408     public function test_restricted_pages_not_visible_in_book_navigation_on_pages()
409     {
410         $chapter = \BookStack\Chapter::first();
411         $page = $chapter->pages->first();
412         $page2 = $chapter->pages[2];
413
414         $this->setEntityRestrictions($page, []);
415
416         $this->actingAs($this->user)
417             ->visit($page2->getUrl())
418             ->dontSeeInElement('.sidebar-page-list', $page->name);
419     }
420
421     public function test_restricted_pages_not_visible_in_book_navigation_on_chapters()
422     {
423         $chapter = \BookStack\Chapter::first();
424         $page = $chapter->pages->first();
425
426         $this->setEntityRestrictions($page, []);
427
428         $this->actingAs($this->user)
429             ->visit($chapter->getUrl())
430             ->dontSeeInElement('.sidebar-page-list', $page->name);
431     }
432
433     public function test_restricted_pages_not_visible_on_chapter_pages()
434     {
435         $chapter = \BookStack\Chapter::first();
436         $page = $chapter->pages->first();
437
438         $this->setEntityRestrictions($page, []);
439
440         $this->actingAs($this->user)
441             ->visit($chapter->getUrl())
442             ->dontSee($page->name);
443     }
444
445     public function test_book_create_restriction_override()
446     {
447         $book = Book::first();
448
449         $bookUrl = $book->getUrl();
450         $this->actingAs($this->viewer)
451             ->visit($bookUrl)
452             ->dontSeeInElement('.action-buttons', 'New Page')
453             ->dontSeeInElement('.action-buttons', 'New Chapter');
454
455         $this->setEntityRestrictions($book, ['view', 'delete', 'update']);
456
457         $this->forceVisit($bookUrl . '/chapter/create')
458             ->see('You do not have permission')->seePageIs('/');
459         $this->forceVisit($bookUrl . '/page/create')
460             ->see('You do not have permission')->seePageIs('/');
461         $this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
462             ->dontSeeInElement('.action-buttons', 'New Chapter');
463
464         $this->setEntityRestrictions($book, ['view', 'create']);
465
466         $this->visit($bookUrl . '/chapter/create')
467             ->type('test chapter', 'name')
468             ->type('test description for chapter', 'description')
469             ->press('Save Chapter')
470             ->seePageIs($bookUrl . '/chapter/test-chapter');
471         $this->visit($bookUrl . '/page/create')
472             ->type('test page', 'name')
473             ->type('test content', 'html')
474             ->press('Save Page')
475             ->seePageIs($bookUrl . '/page/test-page');
476         $this->visit($bookUrl)->seeInElement('.action-buttons', 'New Page')
477             ->seeInElement('.action-buttons', 'New Chapter');
478     }
479
480     public function test_book_update_restriction_override()
481     {
482         $book = Book::first();
483         $bookPage = $book->pages->first();
484         $bookChapter = $book->chapters->first();
485
486         $bookUrl = $book->getUrl();
487         $this->actingAs($this->viewer)
488             ->visit($bookUrl . '/edit')
489             ->dontSee('Edit Book');
490
491         $this->setEntityRestrictions($book, ['view', 'delete']);
492
493         $this->forceVisit($bookUrl . '/edit')
494             ->see('You do not have permission')->seePageIs('/');
495         $this->forceVisit($bookPage->getUrl() . '/edit')
496             ->see('You do not have permission')->seePageIs('/');
497         $this->forceVisit($bookChapter->getUrl() . '/edit')
498             ->see('You do not have permission')->seePageIs('/');
499
500         $this->setEntityRestrictions($book, ['view', 'update']);
501
502         $this->visit($bookUrl . '/edit')
503             ->seePageIs($bookUrl . '/edit');
504         $this->visit($bookPage->getUrl() . '/edit')
505             ->seePageIs($bookPage->getUrl() . '/edit');
506         $this->visit($bookChapter->getUrl() . '/edit')
507             ->see('Edit Chapter');
508     }
509
510     public function test_book_delete_restriction_override()
511     {
512         $book = Book::first();
513         $bookPage = $book->pages->first();
514         $bookChapter = $book->chapters->first();
515
516         $bookUrl = $book->getUrl();
517         $this->actingAs($this->viewer)
518             ->visit($bookUrl . '/delete')
519             ->dontSee('Delete Book');
520
521         $this->setEntityRestrictions($book, ['view', 'update']);
522
523         $this->forceVisit($bookUrl . '/delete')
524             ->see('You do not have permission')->seePageIs('/');
525         $this->forceVisit($bookPage->getUrl() . '/delete')
526             ->see('You do not have permission')->seePageIs('/');
527         $this->forceVisit($bookChapter->getUrl() . '/delete')
528             ->see('You do not have permission')->seePageIs('/');
529
530         $this->setEntityRestrictions($book, ['view', 'delete']);
531
532         $this->visit($bookUrl . '/delete')
533             ->seePageIs($bookUrl . '/delete')->see('Delete Book');
534         $this->visit($bookPage->getUrl() . '/delete')
535             ->seePageIs($bookPage->getUrl() . '/delete')->see('Delete Page');
536         $this->visit($bookChapter->getUrl() . '/delete')
537             ->see('Delete Chapter');
538     }
539
540     public function test_page_visible_if_has_permissions_when_book_not_visible()
541     {
542         $book = Book::first();
543
544         $this->setEntityRestrictions($book, []);
545
546         $bookChapter = $book->chapters->first();
547         $bookPage = $bookChapter->pages->first();
548         $this->setEntityRestrictions($bookPage, ['view']);
549
550         $this->actingAs($this->viewer);
551         $this->get($bookPage->getUrl());
552         $this->assertResponseOk();
553         $this->see($bookPage->name);
554         $this->dontSee(substr($book->name, 0, 15));
555         $this->dontSee(substr($bookChapter->name, 0, 15));
556     }
557
558     public function test_book_sort_view_permission()
559     {
560         $firstBook = Book::first();
561         $secondBook = Book::find(2);
562         $thirdBook = Book::find(3);
563
564         $this->setEntityRestrictions($firstBook, ['view', 'update']);
565         $this->setEntityRestrictions($secondBook, ['view']);
566         $this->setEntityRestrictions($thirdBook, ['view', 'update']);
567
568         // Test sort page visibility
569         $this->actingAs($this->user)->visit($secondBook->getUrl() . '/sort')
570                 ->see('You do not have permission')
571                 ->seePageIs('/');
572
573         // Check sort page on first book
574         $this->actingAs($this->user)->visit($firstBook->getUrl() . '/sort')
575                 ->see($thirdBook->name)
576                 ->dontSee($secondBook->name);
577     }
578
579     public function test_book_sort_permission() {
580         $firstBook = Book::first();
581         $secondBook = Book::find(2);
582
583         $this->setEntityRestrictions($firstBook, ['view', 'update']);
584         $this->setEntityRestrictions($secondBook, ['view']);
585
586         $firstBookChapter = $this->app[EntityRepo::class]->createFromInput('chapter',
587                 ['name' => 'first book chapter'], $firstBook);
588         $secondBookChapter = $this->app[EntityRepo::class]->createFromInput('chapter',
589                 ['name' => 'second book chapter'], $secondBook);
590
591         // Create request data
592         $reqData = [
593             [
594                 'id' => $firstBookChapter->id,
595                 'sort' => 0,
596                 'parentChapter' => false,
597                 'type' => 'chapter',
598                 'book' => $secondBook->id
599             ]
600         ];
601
602         // Move chapter from first book to a second book
603         $this->actingAs($this->user)->put($firstBook->getUrl() . '/sort', ['sort-tree' => json_encode($reqData)])
604                 ->followRedirects()
605                 ->see('You do not have permission')
606                 ->seePageIs('/');
607
608         $reqData = [
609             [
610                 'id' => $secondBookChapter->id,
611                 'sort' => 0,
612                 'parentChapter' => false,
613                 'type' => 'chapter',
614                 'book' => $firstBook->id
615             ]
616         ];
617
618         // Move chapter from second book to first book
619         $this->actingAs($this->user)->put($firstBook->getUrl() . '/sort', ['sort-tree' => json_encode($reqData)])
620                 ->followRedirects()
621                 ->see('You do not have permission')
622                 ->seePageIs('/');
623     }
624 }