]> BookStack Code Mirror - bookstack/commitdiff
Added a whole load of permission & role tests
authorDan Brown <redacted>
Sat, 5 Mar 2016 12:09:09 +0000 (12:09 +0000)
committerDan Brown <redacted>
Sat, 5 Mar 2016 12:09:09 +0000 (12:09 +0000)
app/Activity.php
app/Http/Controllers/UserController.php
app/Repos/BookRepo.php
app/Repos/PermissionsRepo.php
app/User.php
resources/views/partials/activity-item.blade.php
resources/views/settings/roles/form.blade.php
tests/RolesTest.php
tests/TestCase.php

index a1fe608f07da3acea03c44d623dbca984846e10f..ac7c1d749a560de97483deed10939dc07c2868c4 100644 (file)
@@ -15,10 +15,10 @@ class Activity extends Model
 
     /**
      * Get the entity for this activity.
-     * @return bool
      */
     public function entity()
     {
+        if ($this->entity_type === '') $this->entity_type = null;
         return $this->morphTo('entity');
     }
 
index 1207c87f1b02330dea430291c4afc7d13d5d4e87..9f6a4105f1656fc1d5db21c5f4883447a929c164 100644 (file)
@@ -35,6 +35,7 @@ class UserController extends Controller
      */
     public function index()
     {
+        $this->checkPermission('users-manage');
         $users = $this->userRepo->getAllUsers();
         $this->setPageTitle('Users');
         return view('users/index', ['users' => $users]);
index 572030d43701ff2569a69734742d16f75a388ad0..73572f25eb7807011eeeeae329787d5d85ed06df 100644 (file)
@@ -136,7 +136,7 @@ class BookRepo
      */
     public function newFromInput($input)
     {
-        return $this->bookQuery()->fill($input);
+        return $this->book->newInstance($input);
     }
 
     /**
index c35f29d10c96ce53ff6e48507588cce93204bc88..2d497b76a245b0c272014e45aff3f01f2c141019 100644 (file)
@@ -101,6 +101,7 @@ class PermissionsRepo
     public function assignRolePermissions(Role $role, $permissionNameArray = [])
     {
         $permissions = [];
+        $permissionNameArray = array_values($permissionNameArray);
         if ($permissionNameArray && count($permissionNameArray) > 0) {
             $permissions = $this->permission->whereIn('name', $permissionNameArray)->pluck('id')->toArray();
         }
index 2d14c6e6e8a8588c8496818c9ee1b89641af6f55..e1b7c143b267d6fe24d28a1ae8b1f2324c840594 100644 (file)
@@ -67,11 +67,12 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
 
     /**
      * Get all permissions belonging to a the current user.
+     * @param bool $cache
      * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
      */
-    public function permissions()
+    public function permissions($cache = true)
     {
-        if(isset($this->permissions)) return $this->permissions;
+        if(isset($this->permissions) && $cache) return $this->permissions;
         $this->load('roles.permissions');
         $permissions = $this->roles->map(function($role) {
             return $role->permissions;
@@ -106,7 +107,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
      */
     public function attachRoleId($id)
     {
-        $this->roles()->attach([$id]);
+        $this->roles()->attach($id);
     }
 
     /**
index 00ca574dd438fecada15a30476fa4639b79f1c4b..ff0d745866788bffcca5acec04f1d0da8e8fd743 100644 (file)
@@ -16,7 +16,7 @@
 
     {{ $activity->getText() }}
 
-    @if($activity->entity())
+    @if($activity->entity)
         <a href="{{ $activity->entity->getUrl() }}">{{ $activity->entity->name }}</a>
     @endif
 
index 0758f317a608d1de383ba902c485118a56468666..ed0e3dd913470a86b0bd2ba691702caa37c8518e 100644 (file)
@@ -17,7 +17,7 @@
                 <label> @include('settings/roles/checkbox', ['permission' => 'users-manage']) Manage users</label>
             </div>
             <div class="col-md-6">
-                <label>@include('settings/roles/checkbox', ['permission' => 'user-roles-manage']) Manage user roles & Permissions</label>
+                <label>@include('settings/roles/checkbox', ['permission' => 'user-roles-manage']) Manage user roles</label>
             </div>
         </div>
         <hr class="even">
index 7349c29682be833e8311e77eca94897b597f0207..baba208f118ebd60f4affe596b1175bbaa958213 100644 (file)
@@ -7,16 +7,33 @@ class RolesTest extends TestCase
     public function setUp()
     {
         parent::setUp();
+        $this->user = $this->getNewBlankUser();
+    }
+
+    /**
+     * Give the given user some permissions.
+     * @param \BookStack\User $user
+     * @param array $permissions
+     */
+    protected function giveUserPermissions(\BookStack\User $user, $permissions = [])
+    {
+        $newRole = $this->createNewRole($permissions);
+        $user->attachRole($newRole);
+        $user->load('roles');
+        $user->permissions(false);
     }
 
     /**
      * Create a new basic role for testing purposes.
+     * @param array $permissions
      * @return static
      */
-    protected function createNewRole()
+    protected function createNewRole($permissions = [])
     {
         $permissionRepo = app('BookStack\Repos\PermissionsRepo');
-        return $permissionRepo->saveNewRole(factory(\BookStack\Role::class)->make()->toArray());
+        $roleData = factory(\BookStack\Role::class)->make()->toArray();
+        $roleData['permissions'] = array_flip($permissions);
+        return $permissionRepo->saveNewRole($roleData);
     }
 
     public function test_admin_can_see_settings()
@@ -80,4 +97,414 @@ class RolesTest extends TestCase
             ->dontSee($testRoleUpdateName);
     }
 
+    public function test_manage_user_permission()
+    {
+        $this->actingAs($this->user)->visit('/')->visit('/settings/users')
+            ->seePageIs('/');
+        $this->giveUserPermissions($this->user, ['users-manage']);
+        $this->actingAs($this->user)->visit('/')->visit('/settings/users')
+            ->seePageIs('/settings/users');
+    }
+
+    public function test_user_roles_manage_permission()
+    {
+        $this->actingAs($this->user)->visit('/')->visit('/settings/roles')
+            ->seePageIs('/')->visit('/settings/roles/1')->seePageIs('/');
+        $this->giveUserPermissions($this->user, ['user-roles-manage']);
+        $this->actingAs($this->user)->visit('/settings/roles')
+            ->seePageIs('/settings/roles')->click('Admin')
+            ->see('Edit Role');
+    }
+
+    public function test_settings_manage_permission()
+    {
+        $this->actingAs($this->user)->visit('/')->visit('/settings')
+            ->seePageIs('/');
+        $this->giveUserPermissions($this->user, ['settings-manage']);
+        $this->actingAs($this->user)->visit('/')->visit('/settings')
+            ->seePageIs('/settings')->press('Save Settings')->see('Settings Saved');
+    }
+
+    public function test_restrictions_manage_all_permission()
+    {
+        $page = \BookStack\Page::take(1)->get()->first();
+        $this->actingAs($this->user)->visit($page->getUrl())
+            ->dontSee('Restrict')
+            ->visit($page->getUrl() . '/restrict')
+            ->seePageIs('/');
+        $this->giveUserPermissions($this->user, ['restrictions-manage-all']);
+        $this->actingAs($this->user)->visit($page->getUrl())
+            ->see('Restrict')
+            ->click('Restrict')
+            ->see('Page Restrictions')->seePageIs($page->getUrl() . '/restrict');
+    }
+
+    public function test_restrictions_manage_own_permission()
+    {
+        $otherUsersPage = \BookStack\Page::take(1)->get()->first();
+        $content = $this->createEntityChainBelongingToUser($this->user);
+        // Check can't restrict other's content
+        $this->actingAs($this->user)->visit($otherUsersPage->getUrl())
+            ->dontSee('Restrict')
+            ->visit($otherUsersPage->getUrl() . '/restrict')
+            ->seePageIs('/');
+        // Check can't restrict own content
+        $this->actingAs($this->user)->visit($content['page']->getUrl())
+            ->dontSee('Restrict')
+            ->visit($content['page']->getUrl() . '/restrict')
+            ->seePageIs('/');
+
+        $this->giveUserPermissions($this->user, ['restrictions-manage-own']);
+
+        // Check can't restrict other's content
+        $this->actingAs($this->user)->visit($otherUsersPage->getUrl())
+            ->dontSee('Restrict')
+            ->visit($otherUsersPage->getUrl() . '/restrict')
+            ->seePageIs('/');
+        // Check can restrict own content
+        $this->actingAs($this->user)->visit($content['page']->getUrl())
+            ->see('Restrict')
+            ->click('Restrict')
+            ->seePageIs($content['page']->getUrl() . '/restrict');
+    }
+
+    /**
+     * Check a standard entity access permission
+     * @param string $permission
+     * @param array $accessUrls Urls that are only accessible after having the permission
+     * @param array $visibles Check this text, In the buttons toolbar, is only visible with the permission
+     * @param null $callback
+     */
+    private function checkAccessPermission($permission, $accessUrls = [], $visibles = [])
+    {
+        foreach ($accessUrls as $url) {
+            $this->actingAs($this->user)->visit('/')->visit($url)
+                ->seePageIs('/');
+        }
+        foreach ($visibles as $url => $text) {
+            $this->actingAs($this->user)->visit('/')->visit($url)
+                ->dontSeeInElement('.action-buttons',$text);
+        }
+
+        $this->giveUserPermissions($this->user, [$permission]);
+
+        foreach ($accessUrls as $url) {
+            $this->actingAs($this->user)->visit('/')->visit($url)
+                ->seePageIs($url);
+        }
+        foreach ($visibles as $url => $text) {
+            $this->actingAs($this->user)->visit('/')->visit($url)
+                ->see($text);
+        }
+    }
+
+    public function test_books_create_all_permissions()
+    {
+        $this->checkAccessPermission('book-create-all', [
+            '/books/create'
+        ], [
+            '/books' => 'Add new book'
+        ]);
+
+        $this->visit('/books/create')
+            ->type('test book', 'name')
+            ->type('book desc', 'description')
+            ->press('Save Book')
+            ->seePageIs('/books/test-book');
+    }
+
+    public function test_books_edit_own_permission()
+    {
+        $otherBook = \BookStack\Book::take(1)->get()->first();
+        $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
+        $this->checkAccessPermission('book-update-own', [
+            $ownBook->getUrl() . '/edit'
+        ], [
+            $ownBook->getUrl() => 'Edit'
+        ]);
+
+        $this->visit($otherBook->getUrl())
+            ->dontSeeInElement('.action-buttons', 'Edit')
+            ->visit($otherBook->getUrl() . '/edit')
+            ->seePageIs('/');
+    }
+
+    public function test_books_edit_all_permission()
+    {
+        $otherBook = \BookStack\Book::take(1)->get()->first();
+        $this->checkAccessPermission('book-update-all', [
+            $otherBook->getUrl() . '/edit'
+        ], [
+            $otherBook->getUrl() => 'Edit'
+        ]);
+    }
+
+    public function test_books_delete_own_permission()
+    {
+        $this->giveUserPermissions($this->user, ['book-update-all']);
+        $otherBook = \BookStack\Book::take(1)->get()->first();
+        $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
+        $this->checkAccessPermission('book-delete-own', [
+            $ownBook->getUrl() . '/delete'
+        ], [
+            $ownBook->getUrl() => 'Delete'
+        ]);
+
+        $this->visit($otherBook->getUrl())
+            ->dontSeeInElement('.action-buttons', 'Delete')
+            ->visit($otherBook->getUrl() . '/delete')
+            ->seePageIs('/');
+        $this->visit($ownBook->getUrl())->visit($ownBook->getUrl() . '/delete')
+            ->press('Confirm')
+            ->seePageIs('/books')
+            ->dontSee($ownBook->name);
+    }
+
+    public function test_books_delete_all_permission()
+    {
+        $this->giveUserPermissions($this->user, ['book-update-all']);
+        $otherBook = \BookStack\Book::take(1)->get()->first();
+        $this->checkAccessPermission('book-delete-all', [
+            $otherBook->getUrl() . '/delete'
+        ], [
+            $otherBook->getUrl() => 'Delete'
+        ]);
+
+        $this->visit($otherBook->getUrl())->visit($otherBook->getUrl() . '/delete')
+            ->press('Confirm')
+            ->seePageIs('/books')
+            ->dontSee($otherBook->name);
+    }
+
+    public function test_chapter_create_own_permissions()
+    {
+        $book = \BookStack\Book::take(1)->get()->first();
+        $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
+        $baseUrl = $ownBook->getUrl() . '/chapter';
+        $this->checkAccessPermission('chapter-create-own', [
+            $baseUrl . '/create'
+        ], [
+            $ownBook->getUrl() => 'New Chapter'
+        ]);
+
+        $this->visit($baseUrl . '/create')
+            ->type('test chapter', 'name')
+            ->type('chapter desc', 'description')
+            ->press('Save Chapter')
+            ->seePageIs($baseUrl . '/test-chapter');
+
+        $this->visit($book->getUrl())
+            ->dontSeeInElement('.action-buttons', 'New Chapter')
+            ->visit($book->getUrl() . '/chapter/create')
+            ->seePageIs('/');
+    }
+
+    public function test_chapter_create_all_permissions()
+    {
+        $book = \BookStack\Book::take(1)->get()->first();
+        $baseUrl = $book->getUrl() . '/chapter';
+        $this->checkAccessPermission('chapter-create-all', [
+            $baseUrl . '/create'
+        ], [
+            $book->getUrl() => 'New Chapter'
+        ]);
+
+        $this->visit($baseUrl . '/create')
+            ->type('test chapter', 'name')
+            ->type('chapter desc', 'description')
+            ->press('Save Chapter')
+            ->seePageIs($baseUrl . '/test-chapter');
+    }
+
+    public function test_chapter_edit_own_permission()
+    {
+        $otherChapter = \BookStack\Chapter::take(1)->get()->first();
+        $ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
+        $this->checkAccessPermission('chapter-update-own', [
+            $ownChapter->getUrl() . '/edit'
+        ], [
+            $ownChapter->getUrl() => 'Edit'
+        ]);
+
+        $this->visit($otherChapter->getUrl())
+            ->dontSeeInElement('.action-buttons', 'Edit')
+            ->visit($otherChapter->getUrl() . '/edit')
+            ->seePageIs('/');
+    }
+
+    public function test_chapter_edit_all_permission()
+    {
+        $otherChapter = \BookStack\Chapter::take(1)->get()->first();
+        $this->checkAccessPermission('chapter-update-all', [
+            $otherChapter->getUrl() . '/edit'
+        ], [
+            $otherChapter->getUrl() => 'Edit'
+        ]);
+    }
+
+    public function test_chapter_delete_own_permission()
+    {
+        $this->giveUserPermissions($this->user, ['chapter-update-all']);
+        $otherChapter = \BookStack\Chapter::take(1)->get()->first();
+        $ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
+        $this->checkAccessPermission('chapter-delete-own', [
+            $ownChapter->getUrl() . '/delete'
+        ], [
+            $ownChapter->getUrl() => 'Delete'
+        ]);
+
+        $bookUrl = $ownChapter->book->getUrl();
+        $this->visit($otherChapter->getUrl())
+            ->dontSeeInElement('.action-buttons', 'Delete')
+            ->visit($otherChapter->getUrl() . '/delete')
+            ->seePageIs('/');
+        $this->visit($ownChapter->getUrl())->visit($ownChapter->getUrl() . '/delete')
+            ->press('Confirm')
+            ->seePageIs($bookUrl)
+            ->dontSeeInElement('.book-content', $ownChapter->name);
+    }
+
+    public function test_chapter_delete_all_permission()
+    {
+        $this->giveUserPermissions($this->user, ['chapter-update-all']);
+        $otherChapter = \BookStack\Chapter::take(1)->get()->first();
+        $this->checkAccessPermission('chapter-delete-all', [
+            $otherChapter->getUrl() . '/delete'
+        ], [
+            $otherChapter->getUrl() => 'Delete'
+        ]);
+
+        $bookUrl = $otherChapter->book->getUrl();
+        $this->visit($otherChapter->getUrl())->visit($otherChapter->getUrl() . '/delete')
+            ->press('Confirm')
+            ->seePageIs($bookUrl)
+            ->dontSeeInElement('.book-content', $otherChapter->name);
+    }
+
+    public function test_page_create_own_permissions()
+    {
+        $book = \BookStack\Book::take(1)->get()->first();
+        $chapter = \BookStack\Chapter::take(1)->get()->first();
+
+        $entities = $this->createEntityChainBelongingToUser($this->user);
+        $ownBook = $entities['book'];
+        $ownChapter = $entities['chapter'];
+
+        $baseUrl = $ownBook->getUrl() . '/page';
+
+        $this->checkAccessPermission('page-create-own', [
+            $baseUrl . '/create',
+            $ownChapter->getUrl() . '/create-page'
+        ], [
+            $ownBook->getUrl() => 'New Page',
+            $ownChapter->getUrl() => 'New Page'
+        ]);
+
+        $this->visit($baseUrl . '/create')
+            ->type('test page', 'name')
+            ->type('page desc', 'html')
+            ->press('Save Page')
+            ->seePageIs($baseUrl . '/test-page');
+
+        $this->visit($book->getUrl())
+            ->dontSeeInElement('.action-buttons', 'New Page')
+            ->visit($book->getUrl() . '/page/create')
+            ->seePageIs('/');
+        $this->visit($chapter->getUrl())
+            ->dontSeeInElement('.action-buttons', 'New Page')
+            ->visit($chapter->getUrl() . '/create-page')
+            ->seePageIs('/');
+    }
+
+    public function test_page_create_all_permissions()
+    {
+        $book = \BookStack\Book::take(1)->get()->first();
+        $chapter = \BookStack\Chapter::take(1)->get()->first();
+        $baseUrl = $book->getUrl() . '/page';
+        $this->checkAccessPermission('page-create-all', [
+            $baseUrl . '/create',
+            $chapter->getUrl() . '/create-page'
+        ], [
+            $book->getUrl() => 'New Page',
+            $chapter->getUrl() => 'New Page'
+        ]);
+
+        $this->visit($baseUrl . '/create')
+            ->type('test page', 'name')
+            ->type('page desc', 'html')
+            ->press('Save Page')
+            ->seePageIs($baseUrl . '/test-page');
+
+        $this->visit($chapter->getUrl() . '/create-page')
+            ->type('new test page', 'name')
+            ->type('page desc', 'html')
+            ->press('Save Page')
+            ->seePageIs($baseUrl . '/new-test-page');
+    }
+
+    public function test_page_edit_own_permission()
+    {
+        $otherPage = \BookStack\Page::take(1)->get()->first();
+        $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+        $this->checkAccessPermission('page-update-own', [
+            $ownPage->getUrl() . '/edit'
+        ], [
+            $ownPage->getUrl() => 'Edit'
+        ]);
+
+        $this->visit($otherPage->getUrl())
+            ->dontSeeInElement('.action-buttons', 'Edit')
+            ->visit($otherPage->getUrl() . '/edit')
+            ->seePageIs('/');
+    }
+
+    public function test_page_edit_all_permission()
+    {
+        $otherPage = \BookStack\Page::take(1)->get()->first();
+        $this->checkAccessPermission('page-update-all', [
+            $otherPage->getUrl() . '/edit'
+        ], [
+            $otherPage->getUrl() => 'Edit'
+        ]);
+    }
+
+    public function test_page_delete_own_permission()
+    {
+        $this->giveUserPermissions($this->user, ['page-update-all']);
+        $otherPage = \BookStack\Page::take(1)->get()->first();
+        $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+        $this->checkAccessPermission('page-delete-own', [
+            $ownPage->getUrl() . '/delete'
+        ], [
+            $ownPage->getUrl() => 'Delete'
+        ]);
+
+        $bookUrl = $ownPage->book->getUrl();
+        $this->visit($otherPage->getUrl())
+            ->dontSeeInElement('.action-buttons', 'Delete')
+            ->visit($otherPage->getUrl() . '/delete')
+            ->seePageIs('/');
+        $this->visit($ownPage->getUrl())->visit($ownPage->getUrl() . '/delete')
+            ->press('Confirm')
+            ->seePageIs($bookUrl)
+            ->dontSeeInElement('.book-content', $ownPage->name);
+    }
+
+    public function test_page_delete_all_permission()
+    {
+        $this->giveUserPermissions($this->user, ['page-update-all']);
+        $otherPage = \BookStack\Page::take(1)->get()->first();
+        $this->checkAccessPermission('page-delete-all', [
+            $otherPage->getUrl() . '/delete'
+        ], [
+            $otherPage->getUrl() => 'Delete'
+        ]);
+
+        $bookUrl = $otherPage->book->getUrl();
+        $this->visit($otherPage->getUrl())->visit($otherPage->getUrl() . '/delete')
+            ->press('Confirm')
+            ->seePageIs($bookUrl)
+            ->dontSeeInElement('.book-content', $otherPage->name);
+    }
+
 }
index a521fd076ad9cee58c03a3e0db1b134ae5c652cd..840fe0d0894c62a648e2694ce1e17497f9f9571c 100644 (file)
@@ -84,6 +84,17 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
         return $user;
     }
 
+    /**
+     * Quick way to create a new user without any permissions
+     * @param array $attributes
+     * @return mixed
+     */
+    protected function getNewBlankUser($attributes = [])
+    {
+        $user = factory(\BookStack\User::class)->create($attributes);
+        return $user;
+    }
+
     /**
      * Assert that a given string is seen inside an element.
      *