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