Dan Brown

Added a whole load of permission & role tests

...@@ -15,10 +15,10 @@ class Activity extends Model ...@@ -15,10 +15,10 @@ class Activity extends Model
15 15
16 /** 16 /**
17 * Get the entity for this activity. 17 * Get the entity for this activity.
18 - * @return bool
19 */ 18 */
20 public function entity() 19 public function entity()
21 { 20 {
21 + if ($this->entity_type === '') $this->entity_type = null;
22 return $this->morphTo('entity'); 22 return $this->morphTo('entity');
23 } 23 }
24 24
......
...@@ -35,6 +35,7 @@ class UserController extends Controller ...@@ -35,6 +35,7 @@ class UserController extends Controller
35 */ 35 */
36 public function index() 36 public function index()
37 { 37 {
38 + $this->checkPermission('users-manage');
38 $users = $this->userRepo->getAllUsers(); 39 $users = $this->userRepo->getAllUsers();
39 $this->setPageTitle('Users'); 40 $this->setPageTitle('Users');
40 return view('users/index', ['users' => $users]); 41 return view('users/index', ['users' => $users]);
......
...@@ -136,7 +136,7 @@ class BookRepo ...@@ -136,7 +136,7 @@ class BookRepo
136 */ 136 */
137 public function newFromInput($input) 137 public function newFromInput($input)
138 { 138 {
139 - return $this->bookQuery()->fill($input); 139 + return $this->book->newInstance($input);
140 } 140 }
141 141
142 /** 142 /**
......
...@@ -101,6 +101,7 @@ class PermissionsRepo ...@@ -101,6 +101,7 @@ class PermissionsRepo
101 public function assignRolePermissions(Role $role, $permissionNameArray = []) 101 public function assignRolePermissions(Role $role, $permissionNameArray = [])
102 { 102 {
103 $permissions = []; 103 $permissions = [];
104 + $permissionNameArray = array_values($permissionNameArray);
104 if ($permissionNameArray && count($permissionNameArray) > 0) { 105 if ($permissionNameArray && count($permissionNameArray) > 0) {
105 $permissions = $this->permission->whereIn('name', $permissionNameArray)->pluck('id')->toArray(); 106 $permissions = $this->permission->whereIn('name', $permissionNameArray)->pluck('id')->toArray();
106 } 107 }
......
...@@ -67,11 +67,12 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon ...@@ -67,11 +67,12 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
67 67
68 /** 68 /**
69 * Get all permissions belonging to a the current user. 69 * Get all permissions belonging to a the current user.
70 + * @param bool $cache
70 * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough 71 * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
71 */ 72 */
72 - public function permissions() 73 + public function permissions($cache = true)
73 { 74 {
74 - if(isset($this->permissions)) return $this->permissions; 75 + if(isset($this->permissions) && $cache) return $this->permissions;
75 $this->load('roles.permissions'); 76 $this->load('roles.permissions');
76 $permissions = $this->roles->map(function($role) { 77 $permissions = $this->roles->map(function($role) {
77 return $role->permissions; 78 return $role->permissions;
...@@ -106,7 +107,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon ...@@ -106,7 +107,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
106 */ 107 */
107 public function attachRoleId($id) 108 public function attachRoleId($id)
108 { 109 {
109 - $this->roles()->attach([$id]); 110 + $this->roles()->attach($id);
110 } 111 }
111 112
112 /** 113 /**
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
16 16
17 {{ $activity->getText() }} 17 {{ $activity->getText() }}
18 18
19 - @if($activity->entity()) 19 + @if($activity->entity)
20 <a href="{{ $activity->entity->getUrl() }}">{{ $activity->entity->name }}</a> 20 <a href="{{ $activity->entity->getUrl() }}">{{ $activity->entity->name }}</a>
21 @endif 21 @endif
22 22
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
17 <label> @include('settings/roles/checkbox', ['permission' => 'users-manage']) Manage users</label> 17 <label> @include('settings/roles/checkbox', ['permission' => 'users-manage']) Manage users</label>
18 </div> 18 </div>
19 <div class="col-md-6"> 19 <div class="col-md-6">
20 - <label>@include('settings/roles/checkbox', ['permission' => 'user-roles-manage']) Manage user roles & Permissions</label> 20 + <label>@include('settings/roles/checkbox', ['permission' => 'user-roles-manage']) Manage user roles</label>
21 </div> 21 </div>
22 </div> 22 </div>
23 <hr class="even"> 23 <hr class="even">
......
...@@ -7,16 +7,33 @@ class RolesTest extends TestCase ...@@ -7,16 +7,33 @@ class RolesTest extends TestCase
7 public function setUp() 7 public function setUp()
8 { 8 {
9 parent::setUp(); 9 parent::setUp();
10 + $this->user = $this->getNewBlankUser();
11 + }
12 +
13 + /**
14 + * Give the given user some permissions.
15 + * @param \BookStack\User $user
16 + * @param array $permissions
17 + */
18 + protected function giveUserPermissions(\BookStack\User $user, $permissions = [])
19 + {
20 + $newRole = $this->createNewRole($permissions);
21 + $user->attachRole($newRole);
22 + $user->load('roles');
23 + $user->permissions(false);
10 } 24 }
11 25
12 /** 26 /**
13 * Create a new basic role for testing purposes. 27 * Create a new basic role for testing purposes.
28 + * @param array $permissions
14 * @return static 29 * @return static
15 */ 30 */
16 - protected function createNewRole() 31 + protected function createNewRole($permissions = [])
17 { 32 {
18 $permissionRepo = app('BookStack\Repos\PermissionsRepo'); 33 $permissionRepo = app('BookStack\Repos\PermissionsRepo');
19 - return $permissionRepo->saveNewRole(factory(\BookStack\Role::class)->make()->toArray()); 34 + $roleData = factory(\BookStack\Role::class)->make()->toArray();
35 + $roleData['permissions'] = array_flip($permissions);
36 + return $permissionRepo->saveNewRole($roleData);
20 } 37 }
21 38
22 public function test_admin_can_see_settings() 39 public function test_admin_can_see_settings()
...@@ -80,4 +97,414 @@ class RolesTest extends TestCase ...@@ -80,4 +97,414 @@ class RolesTest extends TestCase
80 ->dontSee($testRoleUpdateName); 97 ->dontSee($testRoleUpdateName);
81 } 98 }
82 99
100 + public function test_manage_user_permission()
101 + {
102 + $this->actingAs($this->user)->visit('/')->visit('/settings/users')
103 + ->seePageIs('/');
104 + $this->giveUserPermissions($this->user, ['users-manage']);
105 + $this->actingAs($this->user)->visit('/')->visit('/settings/users')
106 + ->seePageIs('/settings/users');
107 + }
108 +
109 + public function test_user_roles_manage_permission()
110 + {
111 + $this->actingAs($this->user)->visit('/')->visit('/settings/roles')
112 + ->seePageIs('/')->visit('/settings/roles/1')->seePageIs('/');
113 + $this->giveUserPermissions($this->user, ['user-roles-manage']);
114 + $this->actingAs($this->user)->visit('/settings/roles')
115 + ->seePageIs('/settings/roles')->click('Admin')
116 + ->see('Edit Role');
117 + }
118 +
119 + public function test_settings_manage_permission()
120 + {
121 + $this->actingAs($this->user)->visit('/')->visit('/settings')
122 + ->seePageIs('/');
123 + $this->giveUserPermissions($this->user, ['settings-manage']);
124 + $this->actingAs($this->user)->visit('/')->visit('/settings')
125 + ->seePageIs('/settings')->press('Save Settings')->see('Settings Saved');
126 + }
127 +
128 + public function test_restrictions_manage_all_permission()
129 + {
130 + $page = \BookStack\Page::take(1)->get()->first();
131 + $this->actingAs($this->user)->visit($page->getUrl())
132 + ->dontSee('Restrict')
133 + ->visit($page->getUrl() . '/restrict')
134 + ->seePageIs('/');
135 + $this->giveUserPermissions($this->user, ['restrictions-manage-all']);
136 + $this->actingAs($this->user)->visit($page->getUrl())
137 + ->see('Restrict')
138 + ->click('Restrict')
139 + ->see('Page Restrictions')->seePageIs($page->getUrl() . '/restrict');
140 + }
141 +
142 + public function test_restrictions_manage_own_permission()
143 + {
144 + $otherUsersPage = \BookStack\Page::take(1)->get()->first();
145 + $content = $this->createEntityChainBelongingToUser($this->user);
146 + // Check can't restrict other's content
147 + $this->actingAs($this->user)->visit($otherUsersPage->getUrl())
148 + ->dontSee('Restrict')
149 + ->visit($otherUsersPage->getUrl() . '/restrict')
150 + ->seePageIs('/');
151 + // Check can't restrict own content
152 + $this->actingAs($this->user)->visit($content['page']->getUrl())
153 + ->dontSee('Restrict')
154 + ->visit($content['page']->getUrl() . '/restrict')
155 + ->seePageIs('/');
156 +
157 + $this->giveUserPermissions($this->user, ['restrictions-manage-own']);
158 +
159 + // Check can't restrict other's content
160 + $this->actingAs($this->user)->visit($otherUsersPage->getUrl())
161 + ->dontSee('Restrict')
162 + ->visit($otherUsersPage->getUrl() . '/restrict')
163 + ->seePageIs('/');
164 + // Check can restrict own content
165 + $this->actingAs($this->user)->visit($content['page']->getUrl())
166 + ->see('Restrict')
167 + ->click('Restrict')
168 + ->seePageIs($content['page']->getUrl() . '/restrict');
169 + }
170 +
171 + /**
172 + * Check a standard entity access permission
173 + * @param string $permission
174 + * @param array $accessUrls Urls that are only accessible after having the permission
175 + * @param array $visibles Check this text, In the buttons toolbar, is only visible with the permission
176 + * @param null $callback
177 + */
178 + private function checkAccessPermission($permission, $accessUrls = [], $visibles = [])
179 + {
180 + foreach ($accessUrls as $url) {
181 + $this->actingAs($this->user)->visit('/')->visit($url)
182 + ->seePageIs('/');
183 + }
184 + foreach ($visibles as $url => $text) {
185 + $this->actingAs($this->user)->visit('/')->visit($url)
186 + ->dontSeeInElement('.action-buttons',$text);
187 + }
188 +
189 + $this->giveUserPermissions($this->user, [$permission]);
190 +
191 + foreach ($accessUrls as $url) {
192 + $this->actingAs($this->user)->visit('/')->visit($url)
193 + ->seePageIs($url);
194 + }
195 + foreach ($visibles as $url => $text) {
196 + $this->actingAs($this->user)->visit('/')->visit($url)
197 + ->see($text);
198 + }
199 + }
200 +
201 + public function test_books_create_all_permissions()
202 + {
203 + $this->checkAccessPermission('book-create-all', [
204 + '/books/create'
205 + ], [
206 + '/books' => 'Add new book'
207 + ]);
208 +
209 + $this->visit('/books/create')
210 + ->type('test book', 'name')
211 + ->type('book desc', 'description')
212 + ->press('Save Book')
213 + ->seePageIs('/books/test-book');
214 + }
215 +
216 + public function test_books_edit_own_permission()
217 + {
218 + $otherBook = \BookStack\Book::take(1)->get()->first();
219 + $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
220 + $this->checkAccessPermission('book-update-own', [
221 + $ownBook->getUrl() . '/edit'
222 + ], [
223 + $ownBook->getUrl() => 'Edit'
224 + ]);
225 +
226 + $this->visit($otherBook->getUrl())
227 + ->dontSeeInElement('.action-buttons', 'Edit')
228 + ->visit($otherBook->getUrl() . '/edit')
229 + ->seePageIs('/');
230 + }
231 +
232 + public function test_books_edit_all_permission()
233 + {
234 + $otherBook = \BookStack\Book::take(1)->get()->first();
235 + $this->checkAccessPermission('book-update-all', [
236 + $otherBook->getUrl() . '/edit'
237 + ], [
238 + $otherBook->getUrl() => 'Edit'
239 + ]);
240 + }
241 +
242 + public function test_books_delete_own_permission()
243 + {
244 + $this->giveUserPermissions($this->user, ['book-update-all']);
245 + $otherBook = \BookStack\Book::take(1)->get()->first();
246 + $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
247 + $this->checkAccessPermission('book-delete-own', [
248 + $ownBook->getUrl() . '/delete'
249 + ], [
250 + $ownBook->getUrl() => 'Delete'
251 + ]);
252 +
253 + $this->visit($otherBook->getUrl())
254 + ->dontSeeInElement('.action-buttons', 'Delete')
255 + ->visit($otherBook->getUrl() . '/delete')
256 + ->seePageIs('/');
257 + $this->visit($ownBook->getUrl())->visit($ownBook->getUrl() . '/delete')
258 + ->press('Confirm')
259 + ->seePageIs('/books')
260 + ->dontSee($ownBook->name);
261 + }
262 +
263 + public function test_books_delete_all_permission()
264 + {
265 + $this->giveUserPermissions($this->user, ['book-update-all']);
266 + $otherBook = \BookStack\Book::take(1)->get()->first();
267 + $this->checkAccessPermission('book-delete-all', [
268 + $otherBook->getUrl() . '/delete'
269 + ], [
270 + $otherBook->getUrl() => 'Delete'
271 + ]);
272 +
273 + $this->visit($otherBook->getUrl())->visit($otherBook->getUrl() . '/delete')
274 + ->press('Confirm')
275 + ->seePageIs('/books')
276 + ->dontSee($otherBook->name);
277 + }
278 +
279 + public function test_chapter_create_own_permissions()
280 + {
281 + $book = \BookStack\Book::take(1)->get()->first();
282 + $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
283 + $baseUrl = $ownBook->getUrl() . '/chapter';
284 + $this->checkAccessPermission('chapter-create-own', [
285 + $baseUrl . '/create'
286 + ], [
287 + $ownBook->getUrl() => 'New Chapter'
288 + ]);
289 +
290 + $this->visit($baseUrl . '/create')
291 + ->type('test chapter', 'name')
292 + ->type('chapter desc', 'description')
293 + ->press('Save Chapter')
294 + ->seePageIs($baseUrl . '/test-chapter');
295 +
296 + $this->visit($book->getUrl())
297 + ->dontSeeInElement('.action-buttons', 'New Chapter')
298 + ->visit($book->getUrl() . '/chapter/create')
299 + ->seePageIs('/');
300 + }
301 +
302 + public function test_chapter_create_all_permissions()
303 + {
304 + $book = \BookStack\Book::take(1)->get()->first();
305 + $baseUrl = $book->getUrl() . '/chapter';
306 + $this->checkAccessPermission('chapter-create-all', [
307 + $baseUrl . '/create'
308 + ], [
309 + $book->getUrl() => 'New Chapter'
310 + ]);
311 +
312 + $this->visit($baseUrl . '/create')
313 + ->type('test chapter', 'name')
314 + ->type('chapter desc', 'description')
315 + ->press('Save Chapter')
316 + ->seePageIs($baseUrl . '/test-chapter');
317 + }
318 +
319 + public function test_chapter_edit_own_permission()
320 + {
321 + $otherChapter = \BookStack\Chapter::take(1)->get()->first();
322 + $ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
323 + $this->checkAccessPermission('chapter-update-own', [
324 + $ownChapter->getUrl() . '/edit'
325 + ], [
326 + $ownChapter->getUrl() => 'Edit'
327 + ]);
328 +
329 + $this->visit($otherChapter->getUrl())
330 + ->dontSeeInElement('.action-buttons', 'Edit')
331 + ->visit($otherChapter->getUrl() . '/edit')
332 + ->seePageIs('/');
333 + }
334 +
335 + public function test_chapter_edit_all_permission()
336 + {
337 + $otherChapter = \BookStack\Chapter::take(1)->get()->first();
338 + $this->checkAccessPermission('chapter-update-all', [
339 + $otherChapter->getUrl() . '/edit'
340 + ], [
341 + $otherChapter->getUrl() => 'Edit'
342 + ]);
343 + }
344 +
345 + public function test_chapter_delete_own_permission()
346 + {
347 + $this->giveUserPermissions($this->user, ['chapter-update-all']);
348 + $otherChapter = \BookStack\Chapter::take(1)->get()->first();
349 + $ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
350 + $this->checkAccessPermission('chapter-delete-own', [
351 + $ownChapter->getUrl() . '/delete'
352 + ], [
353 + $ownChapter->getUrl() => 'Delete'
354 + ]);
355 +
356 + $bookUrl = $ownChapter->book->getUrl();
357 + $this->visit($otherChapter->getUrl())
358 + ->dontSeeInElement('.action-buttons', 'Delete')
359 + ->visit($otherChapter->getUrl() . '/delete')
360 + ->seePageIs('/');
361 + $this->visit($ownChapter->getUrl())->visit($ownChapter->getUrl() . '/delete')
362 + ->press('Confirm')
363 + ->seePageIs($bookUrl)
364 + ->dontSeeInElement('.book-content', $ownChapter->name);
365 + }
366 +
367 + public function test_chapter_delete_all_permission()
368 + {
369 + $this->giveUserPermissions($this->user, ['chapter-update-all']);
370 + $otherChapter = \BookStack\Chapter::take(1)->get()->first();
371 + $this->checkAccessPermission('chapter-delete-all', [
372 + $otherChapter->getUrl() . '/delete'
373 + ], [
374 + $otherChapter->getUrl() => 'Delete'
375 + ]);
376 +
377 + $bookUrl = $otherChapter->book->getUrl();
378 + $this->visit($otherChapter->getUrl())->visit($otherChapter->getUrl() . '/delete')
379 + ->press('Confirm')
380 + ->seePageIs($bookUrl)
381 + ->dontSeeInElement('.book-content', $otherChapter->name);
382 + }
383 +
384 + public function test_page_create_own_permissions()
385 + {
386 + $book = \BookStack\Book::take(1)->get()->first();
387 + $chapter = \BookStack\Chapter::take(1)->get()->first();
388 +
389 + $entities = $this->createEntityChainBelongingToUser($this->user);
390 + $ownBook = $entities['book'];
391 + $ownChapter = $entities['chapter'];
392 +
393 + $baseUrl = $ownBook->getUrl() . '/page';
394 +
395 + $this->checkAccessPermission('page-create-own', [
396 + $baseUrl . '/create',
397 + $ownChapter->getUrl() . '/create-page'
398 + ], [
399 + $ownBook->getUrl() => 'New Page',
400 + $ownChapter->getUrl() => 'New Page'
401 + ]);
402 +
403 + $this->visit($baseUrl . '/create')
404 + ->type('test page', 'name')
405 + ->type('page desc', 'html')
406 + ->press('Save Page')
407 + ->seePageIs($baseUrl . '/test-page');
408 +
409 + $this->visit($book->getUrl())
410 + ->dontSeeInElement('.action-buttons', 'New Page')
411 + ->visit($book->getUrl() . '/page/create')
412 + ->seePageIs('/');
413 + $this->visit($chapter->getUrl())
414 + ->dontSeeInElement('.action-buttons', 'New Page')
415 + ->visit($chapter->getUrl() . '/create-page')
416 + ->seePageIs('/');
417 + }
418 +
419 + public function test_page_create_all_permissions()
420 + {
421 + $book = \BookStack\Book::take(1)->get()->first();
422 + $chapter = \BookStack\Chapter::take(1)->get()->first();
423 + $baseUrl = $book->getUrl() . '/page';
424 + $this->checkAccessPermission('page-create-all', [
425 + $baseUrl . '/create',
426 + $chapter->getUrl() . '/create-page'
427 + ], [
428 + $book->getUrl() => 'New Page',
429 + $chapter->getUrl() => 'New Page'
430 + ]);
431 +
432 + $this->visit($baseUrl . '/create')
433 + ->type('test page', 'name')
434 + ->type('page desc', 'html')
435 + ->press('Save Page')
436 + ->seePageIs($baseUrl . '/test-page');
437 +
438 + $this->visit($chapter->getUrl() . '/create-page')
439 + ->type('new test page', 'name')
440 + ->type('page desc', 'html')
441 + ->press('Save Page')
442 + ->seePageIs($baseUrl . '/new-test-page');
443 + }
444 +
445 + public function test_page_edit_own_permission()
446 + {
447 + $otherPage = \BookStack\Page::take(1)->get()->first();
448 + $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
449 + $this->checkAccessPermission('page-update-own', [
450 + $ownPage->getUrl() . '/edit'
451 + ], [
452 + $ownPage->getUrl() => 'Edit'
453 + ]);
454 +
455 + $this->visit($otherPage->getUrl())
456 + ->dontSeeInElement('.action-buttons', 'Edit')
457 + ->visit($otherPage->getUrl() . '/edit')
458 + ->seePageIs('/');
459 + }
460 +
461 + public function test_page_edit_all_permission()
462 + {
463 + $otherPage = \BookStack\Page::take(1)->get()->first();
464 + $this->checkAccessPermission('page-update-all', [
465 + $otherPage->getUrl() . '/edit'
466 + ], [
467 + $otherPage->getUrl() => 'Edit'
468 + ]);
469 + }
470 +
471 + public function test_page_delete_own_permission()
472 + {
473 + $this->giveUserPermissions($this->user, ['page-update-all']);
474 + $otherPage = \BookStack\Page::take(1)->get()->first();
475 + $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
476 + $this->checkAccessPermission('page-delete-own', [
477 + $ownPage->getUrl() . '/delete'
478 + ], [
479 + $ownPage->getUrl() => 'Delete'
480 + ]);
481 +
482 + $bookUrl = $ownPage->book->getUrl();
483 + $this->visit($otherPage->getUrl())
484 + ->dontSeeInElement('.action-buttons', 'Delete')
485 + ->visit($otherPage->getUrl() . '/delete')
486 + ->seePageIs('/');
487 + $this->visit($ownPage->getUrl())->visit($ownPage->getUrl() . '/delete')
488 + ->press('Confirm')
489 + ->seePageIs($bookUrl)
490 + ->dontSeeInElement('.book-content', $ownPage->name);
491 + }
492 +
493 + public function test_page_delete_all_permission()
494 + {
495 + $this->giveUserPermissions($this->user, ['page-update-all']);
496 + $otherPage = \BookStack\Page::take(1)->get()->first();
497 + $this->checkAccessPermission('page-delete-all', [
498 + $otherPage->getUrl() . '/delete'
499 + ], [
500 + $otherPage->getUrl() => 'Delete'
501 + ]);
502 +
503 + $bookUrl = $otherPage->book->getUrl();
504 + $this->visit($otherPage->getUrl())->visit($otherPage->getUrl() . '/delete')
505 + ->press('Confirm')
506 + ->seePageIs($bookUrl)
507 + ->dontSeeInElement('.book-content', $otherPage->name);
508 + }
509 +
83 } 510 }
......
...@@ -85,6 +85,17 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase ...@@ -85,6 +85,17 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
85 } 85 }
86 86
87 /** 87 /**
88 + * Quick way to create a new user without any permissions
89 + * @param array $attributes
90 + * @return mixed
91 + */
92 + protected function getNewBlankUser($attributes = [])
93 + {
94 + $user = factory(\BookStack\User::class)->create($attributes);
95 + return $user;
96 + }
97 +
98 + /**
88 * Assert that a given string is seen inside an element. 99 * Assert that a given string is seen inside an element.
89 * 100 *
90 * @param bool|string|null $element 101 * @param bool|string|null $element
......