4 use BookStack\Services\PermissionService;
6 use BookStack\Repos\EntityRepo;
8 class RestrictionsTest extends BrowserKitTest
22 * @var PermissionService
24 protected $permissionService;
26 public function setUp()
29 $this->user = $this->getEditor();
30 $this->viewer = $this->getViewer();
31 $this->permissionService = $this->app[PermissionService::class];
35 * Manually set some permissions on an entity.
36 * @param \BookStack\Entity $entity
39 protected function setEntityRestrictions(\BookStack\Entity $entity, $actions)
41 $entity->restricted = true;
42 $entity->permissions()->delete();
44 $role = $this->user->roles->first();
45 $viewerRole = $this->viewer->roles->first();
48 foreach ($actions as $action) {
50 'role_id' => $role->id,
51 'action' => strtolower($action)
54 'role_id' => $viewerRole->id,
55 'action' => strtolower($action)
58 $entity->permissions()->createMany($permissions);
61 $entity->load('permissions');
62 $this->permissionService->buildJointPermissionsForEntity($entity);
63 $entity->load('jointPermissions');
66 public function test_book_view_restriction()
68 $book = Book::first();
69 $bookPage = $book->pages->first();
70 $bookChapter = $book->chapters->first();
72 $bookUrl = $book->getUrl();
73 $this->actingAs($this->user)
75 ->seePageIs($bookUrl);
77 $this->setEntityRestrictions($book, []);
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');
86 $this->setEntityRestrictions($book, ['view']);
88 $this->visit($bookUrl)
90 $this->visit($bookPage->getUrl())
91 ->see($bookPage->name);
92 $this->visit($bookChapter->getUrl())
93 ->see($bookChapter->name);
96 public function test_book_create_restriction()
98 $book = Book::first();
100 $bookUrl = $book->getUrl();
101 $this->actingAs($this->viewer)
103 ->dontSeeInElement('.action-buttons', 'New Page')
104 ->dontSeeInElement('.action-buttons', 'New Chapter');
105 $this->actingAs($this->user)
107 ->seeInElement('.action-buttons', 'New Page')
108 ->seeInElement('.action-buttons', 'New Chapter');
110 $this->setEntityRestrictions($book, ['view', 'delete', 'update']);
112 $this->forceVisit($bookUrl . '/create-chapter')
113 ->see('You do not have permission')->seePageIs('/');
114 $this->forceVisit($bookUrl . '/create-page')
115 ->see('You do not have permission')->seePageIs('/');
116 $this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
117 ->dontSeeInElement('.action-buttons', 'New Chapter');
119 $this->setEntityRestrictions($book, ['view', 'create']);
121 $this->visit($bookUrl . '/create-chapter')
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 . '/create-page')
127 ->type('test page', 'name')
128 ->type('test content', 'html')
130 ->seePageIs($bookUrl . '/page/test-page');
131 $this->visit($bookUrl)->seeInElement('.action-buttons', 'New Page')
132 ->seeInElement('.action-buttons', 'New Chapter');
135 public function test_book_update_restriction()
137 $book = Book::first();
138 $bookPage = $book->pages->first();
139 $bookChapter = $book->chapters->first();
141 $bookUrl = $book->getUrl();
142 $this->actingAs($this->user)
143 ->visit($bookUrl . '/edit')
146 $this->setEntityRestrictions($book, ['view', 'delete']);
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('/');
155 $this->setEntityRestrictions($book, ['view', 'update']);
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');
165 public function test_book_delete_restriction()
167 $book = Book::first();
168 $bookPage = $book->pages->first();
169 $bookChapter = $book->chapters->first();
171 $bookUrl = $book->getUrl();
172 $this->actingAs($this->user)
173 ->visit($bookUrl . '/delete')
174 ->see('Delete Book');
176 $this->setEntityRestrictions($book, ['view', 'update']);
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('/');
185 $this->setEntityRestrictions($book, ['view', 'delete']);
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');
195 public function test_chapter_view_restriction()
197 $chapter = \BookStack\Chapter::first();
198 $chapterPage = $chapter->pages->first();
200 $chapterUrl = $chapter->getUrl();
201 $this->actingAs($this->user)
203 ->seePageIs($chapterUrl);
205 $this->setEntityRestrictions($chapter, []);
207 $this->forceVisit($chapterUrl)
208 ->see('Chapter not found');
209 $this->forceVisit($chapterPage->getUrl())
210 ->see('Page not found');
212 $this->setEntityRestrictions($chapter, ['view']);
214 $this->visit($chapterUrl)
215 ->see($chapter->name);
216 $this->visit($chapterPage->getUrl())
217 ->see($chapterPage->name);
220 public function test_chapter_create_restriction()
222 $chapter = \BookStack\Chapter::first();
224 $chapterUrl = $chapter->getUrl();
225 $this->actingAs($this->user)
227 ->seeInElement('.action-buttons', 'New Page');
229 $this->setEntityRestrictions($chapter, ['view', 'delete', 'update']);
231 $this->forceVisit($chapterUrl . '/create-page')
232 ->see('You do not have permission')->seePageIs('/');
233 $this->visit($chapterUrl)->dontSeeInElement('.action-buttons', 'New Page');
235 $this->setEntityRestrictions($chapter, ['view', 'create']);
238 $this->visit($chapterUrl . '/create-page')
239 ->type('test page', 'name')
240 ->type('test content', 'html')
242 ->seePageIs($chapter->book->getUrl() . '/page/test-page');
244 $this->visit($chapterUrl)->seeInElement('.action-buttons', 'New Page');
247 public function test_chapter_update_restriction()
249 $chapter = \BookStack\Chapter::first();
250 $chapterPage = $chapter->pages->first();
252 $chapterUrl = $chapter->getUrl();
253 $this->actingAs($this->user)
254 ->visit($chapterUrl . '/edit')
255 ->see('Edit Chapter');
257 $this->setEntityRestrictions($chapter, ['view', 'delete']);
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('/');
264 $this->setEntityRestrictions($chapter, ['view', 'update']);
266 $this->visit($chapterUrl . '/edit')
267 ->seePageIs($chapterUrl . '/edit')->see('Edit Chapter');
268 $this->visit($chapterPage->getUrl() . '/edit')
269 ->seePageIs($chapterPage->getUrl() . '/edit');
272 public function test_chapter_delete_restriction()
274 $chapter = \BookStack\Chapter::first();
275 $chapterPage = $chapter->pages->first();
277 $chapterUrl = $chapter->getUrl();
278 $this->actingAs($this->user)
279 ->visit($chapterUrl . '/delete')
280 ->see('Delete Chapter');
282 $this->setEntityRestrictions($chapter, ['view', 'update']);
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('/');
289 $this->setEntityRestrictions($chapter, ['view', 'delete']);
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');
297 public function test_page_view_restriction()
299 $page = \BookStack\Page::first();
301 $pageUrl = $page->getUrl();
302 $this->actingAs($this->user)
304 ->seePageIs($pageUrl);
306 $this->setEntityRestrictions($page, ['update', 'delete']);
308 $this->forceVisit($pageUrl)
309 ->see('Page not found');
311 $this->setEntityRestrictions($page, ['view']);
313 $this->visit($pageUrl)
317 public function test_page_update_restriction()
319 $page = \BookStack\Chapter::first();
321 $pageUrl = $page->getUrl();
322 $this->actingAs($this->user)
323 ->visit($pageUrl . '/edit')
324 ->seeInField('name', $page->name);
326 $this->setEntityRestrictions($page, ['view', 'delete']);
328 $this->forceVisit($pageUrl . '/edit')
329 ->see('You do not have permission')->seePageIs('/');
331 $this->setEntityRestrictions($page, ['view', 'update']);
333 $this->visit($pageUrl . '/edit')
334 ->seePageIs($pageUrl . '/edit')->seeInField('name', $page->name);
337 public function test_page_delete_restriction()
339 $page = \BookStack\Page::first();
341 $pageUrl = $page->getUrl();
342 $this->actingAs($this->user)
343 ->visit($pageUrl . '/delete')
344 ->see('Delete Page');
346 $this->setEntityRestrictions($page, ['view', 'update']);
348 $this->forceVisit($pageUrl . '/delete')
349 ->see('You do not have permission')->seePageIs('/');
351 $this->setEntityRestrictions($page, ['view', 'delete']);
353 $this->visit($pageUrl . '/delete')
354 ->seePageIs($pageUrl . '/delete')->see('Delete Page');
357 public function test_book_restriction_form()
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',
374 public function test_chapter_restriction_form()
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',
391 public function test_page_restriction_form()
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',
408 public function test_restricted_pages_not_visible_in_book_navigation_on_pages()
410 $chapter = \BookStack\Chapter::first();
411 $page = $chapter->pages->first();
412 $page2 = $chapter->pages[2];
414 $this->setEntityRestrictions($page, []);
416 $this->actingAs($this->user)
417 ->visit($page2->getUrl())
418 ->dontSeeInElement('.sidebar-page-list', $page->name);
421 public function test_restricted_pages_not_visible_in_book_navigation_on_chapters()
423 $chapter = \BookStack\Chapter::first();
424 $page = $chapter->pages->first();
426 $this->setEntityRestrictions($page, []);
428 $this->actingAs($this->user)
429 ->visit($chapter->getUrl())
430 ->dontSeeInElement('.sidebar-page-list', $page->name);
433 public function test_restricted_pages_not_visible_on_chapter_pages()
435 $chapter = \BookStack\Chapter::first();
436 $page = $chapter->pages->first();
438 $this->setEntityRestrictions($page, []);
440 $this->actingAs($this->user)
441 ->visit($chapter->getUrl())
442 ->dontSee($page->name);
445 public function test_book_create_restriction_override()
447 $book = Book::first();
449 $bookUrl = $book->getUrl();
450 $this->actingAs($this->viewer)
452 ->dontSeeInElement('.action-buttons', 'New Page')
453 ->dontSeeInElement('.action-buttons', 'New Chapter');
455 $this->setEntityRestrictions($book, ['view', 'delete', 'update']);
457 $this->forceVisit($bookUrl . '/create-chapter')
458 ->see('You do not have permission')->seePageIs('/');
459 $this->forceVisit($bookUrl . '/create-page')
460 ->see('You do not have permission')->seePageIs('/');
461 $this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
462 ->dontSeeInElement('.action-buttons', 'New Chapter');
464 $this->setEntityRestrictions($book, ['view', 'create']);
466 $this->visit($bookUrl . '/create-chapter')
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 . '/create-page')
472 ->type('test page', 'name')
473 ->type('test content', 'html')
475 ->seePageIs($bookUrl . '/page/test-page');
476 $this->visit($bookUrl)->seeInElement('.action-buttons', 'New Page')
477 ->seeInElement('.action-buttons', 'New Chapter');
480 public function test_book_update_restriction_override()
482 $book = Book::first();
483 $bookPage = $book->pages->first();
484 $bookChapter = $book->chapters->first();
486 $bookUrl = $book->getUrl();
487 $this->actingAs($this->viewer)
488 ->visit($bookUrl . '/edit')
489 ->dontSee('Edit Book');
491 $this->setEntityRestrictions($book, ['view', 'delete']);
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('/');
500 $this->setEntityRestrictions($book, ['view', 'update']);
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');
510 public function test_book_delete_restriction_override()
512 $book = Book::first();
513 $bookPage = $book->pages->first();
514 $bookChapter = $book->chapters->first();
516 $bookUrl = $book->getUrl();
517 $this->actingAs($this->viewer)
518 ->visit($bookUrl . '/delete')
519 ->dontSee('Delete Book');
521 $this->setEntityRestrictions($book, ['view', 'update']);
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('/');
530 $this->setEntityRestrictions($book, ['view', 'delete']);
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');
540 public function test_page_visible_if_has_permissions_when_book_not_visible()
542 $book = Book::first();
544 $this->setEntityRestrictions($book, []);
546 $bookChapter = $book->chapters->first();
547 $bookPage = $bookChapter->pages->first();
548 $this->setEntityRestrictions($bookPage, ['view']);
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));
558 public function test_book_sort_view_permission()
560 $firstBook = Book::first();
561 $secondBook = Book::find(2);
562 $thirdBook = Book::find(3);
564 $this->setEntityRestrictions($firstBook, ['view', 'update']);
565 $this->setEntityRestrictions($secondBook, ['view']);
566 $this->setEntityRestrictions($thirdBook, ['view', 'update']);
568 // Test sort page visibility
569 $this->actingAs($this->user)->visit($secondBook->getUrl() . '/sort')
570 ->see('You do not have permission')
573 // Check sort page on first book
574 $this->actingAs($this->user)->visit($firstBook->getUrl() . '/sort')
575 ->see($thirdBook->name)
576 ->dontSee($secondBook->name);
579 public function test_book_sort_permission() {
580 $firstBook = Book::first();
581 $secondBook = Book::find(2);
583 $this->setEntityRestrictions($firstBook, ['view', 'update']);
584 $this->setEntityRestrictions($secondBook, ['view']);
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);
591 // Create request data
594 'id' => $firstBookChapter->id,
596 'parentChapter' => false,
598 'book' => $secondBook->id
602 // Move chapter from first book to a second book
603 $this->actingAs($this->user)->put($firstBook->getUrl() . '/sort', ['sort-tree' => json_encode($reqData)])
605 ->see('You do not have permission')
610 'id' => $secondBookChapter->id,
612 'parentChapter' => false,
614 'book' => $firstBook->id
618 // Move chapter from second book to first book
619 $this->actingAs($this->user)->put($firstBook->getUrl() . '/sort', ['sort-tree' => json_encode($reqData)])
621 ->see('You do not have permission')