Skip to content

Commit cdb1c7e

Browse files
committed
Added destination permission checking to entity move
1 parent 0f7b0ad commit cdb1c7e

9 files changed

Lines changed: 193 additions & 211 deletions

File tree

app/Http/Controllers/PageController.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,8 @@ public function move($bookSlug, $pageSlug, Request $request)
585585
return redirect()->back();
586586
}
587587

588+
$this->checkOwnablePermission('page-create', $parent);
589+
588590
$this->entityRepo->changePageParent($page, $parent);
589591
Activity::add($page, 'page_move', $page->book->id);
590592
session()->flash('success', trans('entities.pages_move_success', ['parentName' => $parent->name]));

resources/views/chapters/move.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
{!! csrf_field() !!}
1818
<input type="hidden" name="_method" value="PUT">
1919

20-
@include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book'])
20+
@include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book', 'entityPermission' => 'chapter-create'])
2121

2222
<div class="form-group text-right">
2323
<a href="{{ $chapter->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>

resources/views/pages/move.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
{!! csrf_field() !!}
1818
<input type="hidden" name="_method" value="PUT">
1919

20-
@include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter'])
20+
@include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter', 'entityPermission' => 'page-create'])
2121

2222
<div class="form-group text-right">
2323
<a href="{{ $page->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>

tests/BrowserKitTest.php

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ abstract class BrowserKitTest extends TestCase
1212
{
1313

1414
use DatabaseTransactions;
15-
16-
// Local user instances
17-
private $admin;
18-
private $editor;
15+
use SharedTestHelpers;
1916

2017
/**
2118
* The base URL to use while testing the application.
@@ -43,38 +40,6 @@ public function createApplication()
4340
return $app;
4441
}
4542

46-
/**
47-
* Set the current user context to be an admin.
48-
* @return $this
49-
*/
50-
public function asAdmin()
51-
{
52-
return $this->actingAs($this->getAdmin());
53-
}
54-
55-
/**
56-
* Get the current admin user.
57-
* @return mixed
58-
*/
59-
public function getAdmin() {
60-
if($this->admin === null) {
61-
$adminRole = Role::getSystemRole('admin');
62-
$this->admin = $adminRole->users->first();
63-
}
64-
return $this->admin;
65-
}
66-
67-
/**
68-
* Set the current editor context to be an editor.
69-
* @return $this
70-
*/
71-
public function asEditor()
72-
{
73-
if ($this->editor === null) {
74-
$this->editor = $this->getEditor();
75-
}
76-
return $this->actingAs($this->editor);
77-
}
7843

7944
/**
8045
* Get a user that's not a system user such as the guest user.
@@ -127,28 +92,6 @@ protected function updateEntityPermissions(Entity $entity)
12792
$restrictionService->buildJointPermissionsForEntity($entity);
12893
}
12994

130-
/**
131-
* Get an instance of a user with 'editor' permissions
132-
* @param array $attributes
133-
* @return mixed
134-
*/
135-
protected function getEditor($attributes = [])
136-
{
137-
$user = \BookStack\Role::getRole('editor')->users()->first();
138-
if (!empty($attributes)) $user->forceFill($attributes)->save();
139-
return $user;
140-
}
141-
142-
/**
143-
* Get an instance of a user with 'viewer' permissions
144-
* @return mixed
145-
*/
146-
protected function getViewer()
147-
{
148-
$user = \BookStack\Role::getRole('viewer')->users()->first();
149-
if (!empty($attributes)) $user->forceFill($attributes)->save();
150-
return $user;
151-
}
15295

15396
/**
15497
* Quick way to create a new user without any permissions

tests/Entity/SortTest.php

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ public function test_page_move()
3434
$currentBook = $page->book;
3535
$newBook = Book::where('id', '!=', $currentBook->id)->first();
3636

37-
$resp = $this->asAdmin()->get($page->getUrl() . '/move');
37+
$resp = $this->asEditor()->get($page->getUrl('/move'));
3838
$resp->assertSee('Move Page');
3939

40-
$movePageResp = $this->put($page->getUrl() . '/move', [
40+
$movePageResp = $this->put($page->getUrl('/move'), [
4141
'entity_selection' => 'book:' . $newBook->id
4242
]);
4343
$page = Page::find($page->id);
@@ -50,17 +50,42 @@ public function test_page_move()
5050
$newBookResp->assertSee($page->name);
5151
}
5252

53+
public function test_page_move_requires_create_permissions_on_parent()
54+
{
55+
$page = Page::first();
56+
$currentBook = $page->book;
57+
$newBook = Book::where('id', '!=', $currentBook->id)->first();
58+
$editor = $this->getEditor();
59+
60+
$this->setEntityRestrictions($newBook, ['view', 'edit', 'delete'], $editor->roles);
61+
62+
$movePageResp = $this->actingAs($editor)->put($page->getUrl('/move'), [
63+
'entity_selection' => 'book:' . $newBook->id
64+
]);
65+
$this->assertPermissionError($movePageResp);
66+
67+
$this->setEntityRestrictions($newBook, ['view', 'edit', 'delete', 'create'], $editor->roles);
68+
$movePageResp = $this->put($page->getUrl('/move'), [
69+
'entity_selection' => 'book:' . $newBook->id
70+
]);
71+
72+
$page = Page::find($page->id);
73+
$movePageResp->assertRedirect($page->getUrl());
74+
75+
$this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book');
76+
}
77+
5378
public function test_chapter_move()
5479
{
5580
$chapter = Chapter::first();
5681
$currentBook = $chapter->book;
5782
$pageToCheck = $chapter->pages->first();
5883
$newBook = Book::where('id', '!=', $currentBook->id)->first();
5984

60-
$chapterMoveResp = $this->asAdmin()->get($chapter->getUrl() . '/move');
85+
$chapterMoveResp = $this->asEditor()->get($chapter->getUrl('/move'));
6186
$chapterMoveResp->assertSee('Move Chapter');
6287

63-
$moveChapterResp = $this->put($chapter->getUrl() . '/move', [
88+
$moveChapterResp = $this->put($chapter->getUrl('/move'), [
6489
'entity_selection' => 'book:' . $newBook->id
6590
]);
6691

@@ -105,7 +130,7 @@ public function test_book_sort()
105130
];
106131
}
107132

108-
$sortResp = $this->asAdmin()->put($newBook->getUrl() . '/sort', ['sort-tree' => json_encode($reqData)]);
133+
$sortResp = $this->asEditor()->put($newBook->getUrl() . '/sort', ['sort-tree' => json_encode($reqData)]);
109134
$sortResp->assertRedirect($newBook->getUrl());
110135
$sortResp->assertStatus(302);
111136
$this->assertDatabaseHas('chapters', [

tests/Permissions/RestrictionsTest.php

Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php namespace Tests;
22

33
use BookStack\Book;
4-
use BookStack\Services\PermissionService;
4+
use BookStack\Entity;
55
use BookStack\User;
66
use BookStack\Repos\EntityRepo;
77

@@ -18,49 +18,20 @@ class RestrictionsTest extends BrowserKitTest
1818
*/
1919
protected $viewer;
2020

21-
/**
22-
* @var PermissionService
23-
*/
24-
protected $permissionService;
25-
2621
public function setUp()
2722
{
2823
parent::setUp();
2924
$this->user = $this->getEditor();
3025
$this->viewer = $this->getViewer();
31-
$this->permissionService = $this->app[PermissionService::class];
3226
}
3327

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)
28+
protected function setEntityRestrictions(Entity $entity, $actions = [], $roles = [])
4029
{
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');
30+
$roles = [
31+
$this->user->roles->first(),
32+
$this->viewer->roles->first(),
33+
];
34+
parent::setEntityRestrictions($entity, $actions, $roles);
6435
}
6536

6637
public function test_book_view_restriction()

tests/Permissions/RolesTest.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,6 @@ public function setUp()
1616
$this->user = $this->getViewer();
1717
}
1818

19-
protected function getViewer()
20-
{
21-
$role = \BookStack\Role::getRole('viewer');
22-
$viewer = $this->getNewBlankUser();
23-
$viewer->attachRole($role);;
24-
return $viewer;
25-
}
26-
2719
/**
2820
* Give the given user some permissions.
2921
* @param \BookStack\User $user

tests/SharedTestHelpers.php

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?php namespace Tests;
2+
3+
use BookStack\Book;
4+
use BookStack\Chapter;
5+
use BookStack\Entity;
6+
use BookStack\Repos\EntityRepo;
7+
use BookStack\Role;
8+
use BookStack\Services\PermissionService;
9+
use BookStack\Services\SettingService;
10+
11+
trait SharedTestHelpers
12+
{
13+
14+
protected $admin;
15+
protected $editor;
16+
17+
/**
18+
* Set the current user context to be an admin.
19+
* @return $this
20+
*/
21+
public function asAdmin()
22+
{
23+
return $this->actingAs($this->getAdmin());
24+
}
25+
26+
/**
27+
* Get the current admin user.
28+
* @return mixed
29+
*/
30+
public function getAdmin() {
31+
if($this->admin === null) {
32+
$adminRole = Role::getSystemRole('admin');
33+
$this->admin = $adminRole->users->first();
34+
}
35+
return $this->admin;
36+
}
37+
38+
/**
39+
* Set the current user context to be an editor.
40+
* @return $this
41+
*/
42+
public function asEditor()
43+
{
44+
return $this->actingAs($this->getEditor());
45+
}
46+
47+
48+
/**
49+
* Get a editor user.
50+
* @return mixed
51+
*/
52+
protected function getEditor() {
53+
if($this->editor === null) {
54+
$editorRole = Role::getRole('editor');
55+
$this->editor = $editorRole->users->first();
56+
}
57+
return $this->editor;
58+
}
59+
60+
/**
61+
* Get an instance of a user with 'viewer' permissions
62+
* @param $attributes
63+
* @return mixed
64+
*/
65+
protected function getViewer($attributes = [])
66+
{
67+
$user = \BookStack\Role::getRole('viewer')->users()->first();
68+
if (!empty($attributes)) $user->forceFill($attributes)->save();
69+
return $user;
70+
}
71+
72+
/**
73+
* Create and return a new book.
74+
* @param array $input
75+
* @return Book
76+
*/
77+
public function newBook($input = ['name' => 'test book', 'description' => 'My new test book']) {
78+
return $this->app[EntityRepo::class]->createFromInput('book', $input, false);
79+
}
80+
81+
/**
82+
* Create and return a new test chapter
83+
* @param array $input
84+
* @param Book $book
85+
* @return Chapter
86+
*/
87+
public function newChapter($input = ['name' => 'test chapter', 'description' => 'My new test chapter'], Book $book) {
88+
return $this->app[EntityRepo::class]->createFromInput('chapter', $input, $book);
89+
}
90+
91+
/**
92+
* Create and return a new test page
93+
* @param array $input
94+
* @return Chapter
95+
*/
96+
public function newPage($input = ['name' => 'test page', 'html' => 'My new test page']) {
97+
$book = Book::first();
98+
$entityRepo = $this->app[EntityRepo::class];
99+
$draftPage = $entityRepo->getDraftPage($book);
100+
return $entityRepo->publishPageDraft($draftPage, $input);
101+
}
102+
103+
/**
104+
* Quickly sets an array of settings.
105+
* @param $settingsArray
106+
*/
107+
protected function setSettings($settingsArray)
108+
{
109+
$settings = app(SettingService::class);
110+
foreach ($settingsArray as $key => $value) {
111+
$settings->put($key, $value);
112+
}
113+
}
114+
115+
/**
116+
* Manually set some permissions on an entity.
117+
* @param Entity $entity
118+
* @param array $actions
119+
* @param array $roles
120+
*/
121+
protected function setEntityRestrictions(Entity $entity, $actions = [], $roles = [])
122+
{
123+
$entity->restricted = true;
124+
$entity->permissions()->delete();
125+
126+
$permissions = [];
127+
foreach ($actions as $action) {
128+
foreach ($roles as $role) {
129+
$permissions[] = [
130+
'role_id' => $role->id,
131+
'action' => strtolower($action)
132+
];
133+
}
134+
}
135+
$entity->permissions()->createMany($permissions);
136+
137+
$entity->save();
138+
$entity->load('permissions');
139+
$this->app[PermissionService::class]->buildJointPermissionsForEntity($entity);
140+
$entity->load('jointPermissions');
141+
}
142+
143+
}

0 commit comments

Comments
 (0)