]> BookStack Code Mirror - bookstack/commitdiff
Added hidden public role to fit with new permissions system
authorDan Brown <redacted>
Sun, 1 May 2016 18:36:53 +0000 (19:36 +0100)
committerDan Brown <redacted>
Sun, 1 May 2016 18:36:53 +0000 (19:36 +0100)
14 files changed:
app/Http/Controllers/PermissionController.php
app/Http/Controllers/UserController.php
app/Repos/PermissionsRepo.php
app/Repos/UserRepo.php
app/Role.php
app/Services/RestrictionService.php
app/helpers.php
database/migrations/2016_04_20_192649_create_entity_permissions_table.php
resources/views/chapters/show.blade.php
resources/views/settings/index.blade.php
resources/views/settings/roles/form.blade.php
resources/views/users/forms/ldap.blade.php
resources/views/users/forms/standard.blade.php
tests/Permissions/RolesTest.php

index 3f2eb7f9995271f3e062ff05baa4901815f137e2..22d0cfe0ee5082e1471d169fb49eb4ed4d06ec5a 100644 (file)
@@ -63,11 +63,13 @@ class PermissionController extends Controller
      * Show the form for editing a user role.
      * @param $id
      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
+     * @throws PermissionsException
      */
     public function editRole($id)
     {
         $this->checkPermission('user-roles-manage');
         $role = $this->permissionsRepo->getRoleById($id);
+        if ($role->hidden) throw new PermissionsException('This role cannot be edited');
         return view('settings/roles/edit', ['role' => $role]);
     }
 
index d599316409cda20a0a273e26f3f0ca5ebc4c4608..6956b8d18e0937cd3455d684c4b738cf64d184b4 100644 (file)
@@ -49,7 +49,8 @@ class UserController extends Controller
     {
         $this->checkPermission('users-manage');
         $authMethod = config('auth.method');
-        return view('users/create', ['authMethod' => $authMethod]);
+        $roles = $this->userRepo->getAssignableRoles();
+        return view('users/create', ['authMethod' => $authMethod, 'roles' => $roles]);
     }
 
     /**
@@ -117,7 +118,8 @@ class UserController extends Controller
         $user = $this->user->findOrFail($id);
         $activeSocialDrivers = $socialAuthService->getActiveDrivers();
         $this->setPageTitle('User Profile');
-        return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod]);
+        $roles = $this->userRepo->getAssignableRoles();
+        return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod, 'roles' => $roles]);
     }
 
     /**
index ab265a45f58ede14f1f3a36f18ba821ac8feaa92..8bdcc83829bde1ceb8d9873223a1570d81a2141e 100644 (file)
@@ -14,6 +14,8 @@ class PermissionsRepo
     protected $role;
     protected $restrictionService;
 
+    protected $systemRoles = ['admin', 'public'];
+
     /**
      * PermissionsRepo constructor.
      * @param Permission $permission
@@ -33,7 +35,7 @@ class PermissionsRepo
      */
     public function getAllRoles()
     {
-        return $this->role->all();
+        return $this->role->where('hidden', '=', false)->get();
     }
 
     /**
@@ -43,7 +45,7 @@ class PermissionsRepo
      */
     public function getAllRolesExcept(Role $role)
     {
-        return $this->role->where('id', '!=', $role->id)->get();
+        return $this->role->where('id', '!=', $role->id)->where('hidden', '=', false)->get();
     }
 
     /**
@@ -82,10 +84,14 @@ class PermissionsRepo
      * Ensure Admin role always has all permissions.
      * @param $roleId
      * @param $roleData
+     * @throws PermissionsException
      */
     public function updateRole($roleId, $roleData)
     {
         $role = $this->role->findOrFail($roleId);
+
+        if ($role->hidden) throw new PermissionsException("Cannot update a hidden role");
+
         $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
         $this->assignRolePermissions($role, $permissions);
 
@@ -128,8 +134,8 @@ class PermissionsRepo
         $role = $this->role->findOrFail($roleId);
 
         // Prevent deleting admin role or default registration role.
-        if ($role->name === 'admin') {
-            throw new PermissionsException('The admin role cannot be deleted');
+        if ($role->system_name && in_array($role->system_name, $this->systemRoles)) {
+            throw new PermissionsException('This role is a system role and cannot be deleted');
         } else if ($role->id == setting('registration-role')) {
             throw new PermissionsException('This role cannot be deleted while set as the default registration role.');
         }
index 9b5c8d7e7f20c13816ac421664b54192cb7b187a..b4931bdffbd859222238bcefbfa56c0fd3cbb2cb 100644 (file)
@@ -168,6 +168,15 @@ class UserRepo
         ];
     }
 
+    /**
+     * Get the roles in the system that are assignable to a user.
+     * @return mixed
+     */
+    public function getAssignableRoles()
+    {
+        return $this->role->visible();
+    }
+
     /**
      * Get all the roles which can be given restricted access to
      * other entities in the system.
@@ -175,7 +184,7 @@ class UserRepo
      */
     public function getRestrictableRoles()
     {
-        return $this->role->where('name', '!=', 'admin')->get();
+        return $this->role->where('hidden', '=', false)->where('system_name', '=', '')->get();
     }
 
 }
\ No newline at end of file
index 3b930d113259af4105b5e1e68b6cdedb28c12e17..b331e93e43e7cf4b6109a2929be6dd18fa46969f 100644 (file)
@@ -72,4 +72,24 @@ class Role extends Model
     {
         return static::where('name', '=', $roleName)->first();
     }
+
+    /**
+     * Get the role object for the specified system role.
+     * @param $roleName
+     * @return mixed
+     */
+    public static function getSystemRole($roleName)
+    {
+        return static::where('system_name', '=', $roleName)->first();
+    }
+
+    /**
+     * GEt all visible roles
+     * @return mixed
+     */
+    public static function visible()
+    {
+        return static::where('hidden', '=', false)->orderBy('name')->get();
+    }
+
 }
index 40287bf77c426f1a741309ef4ec0f32ec079548b..ca5c6c9c1ca638b5f351bdf05feb61b0975002d7 100644 (file)
@@ -35,9 +35,10 @@ class RestrictionService
     public function __construct(EntityPermission $entityPermission, Book $book, Chapter $chapter, Page $page, Role $role)
     {
         $this->currentUser = auth()->user();
-        if ($this->currentUser === null) $this->currentUser = new User(['id' => 0]);
-        $this->userRoles = $this->currentUser ? $this->currentUser->roles->pluck('id') : [];
-        $this->isAdmin = $this->currentUser ? $this->currentUser->hasRole('admin') : false;
+        $userSet = $this->currentUser !== null;
+        $this->userRoles = false;
+        $this->isAdmin = $userSet ? $this->currentUser->hasRole('admin') : false;
+        if (!$userSet) $this->currentUser = new User();
 
         $this->entityPermission = $entityPermission;
         $this->role = $role;
@@ -46,6 +47,28 @@ class RestrictionService
         $this->page = $page;
     }
 
+    /**
+     * Get the roles for the current user;
+     * @return array|bool
+     */
+    protected function getRoles()
+    {
+        if ($this->userRoles !== false) return $this->userRoles;
+
+        $roles = [];
+
+        if (auth()->guest()) {
+            $roles[] = $this->role->getSystemRole('public')->id;
+            return $roles;
+        }
+
+
+        foreach ($this->currentUser->roles as $role) {
+            $roles[] = $role->id;
+        }
+        return $roles;
+    }
+
     /**
      * Re-generate all entity permission from scratch.
      */
@@ -346,7 +369,7 @@ class RestrictionService
     {
         return $query->where(function ($parentQuery) {
             $parentQuery->whereHas('permissions', function ($permissionQuery) {
-                $permissionQuery->whereIn('role_id', $this->userRoles)
+                $permissionQuery->whereIn('role_id', $this->getRoles())
                     ->where('action', '=', $this->currentAction)
                     ->where(function ($query) {
                         $query->where('has_permission', '=', true)
@@ -428,7 +451,7 @@ class RestrictionService
                     ->whereRaw('entity_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
                     ->whereRaw('entity_permissions.entity_type=' . $tableDetails['tableName'] . '.' . $tableDetails['entityTypeColumn'])
                     ->where('action', '=', $this->currentAction)
-                    ->whereIn('role_id', $this->userRoles)
+                    ->whereIn('role_id', $this->getRoles())
                     ->where(function ($query) {
                         $query->where('has_permission', '=', true)->orWhere(function ($query) {
                             $query->where('has_permission_own', '=', true)
@@ -460,7 +483,7 @@ class RestrictionService
                         ->whereRaw('entity_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
                         ->where('entity_type', '=', 'Bookstack\\Page')
                         ->where('action', '=', $this->currentAction)
-                        ->whereIn('role_id', $this->userRoles)
+                        ->whereIn('role_id', $this->getRoles())
                         ->where(function ($query) {
                             $query->where('has_permission', '=', true)->orWhere(function ($query) {
                                 $query->where('has_permission_own', '=', true)
index 582e4e03ad56242ffabcfd1f11d5537be90ac938..4fa2f2d4deae39fe3500a14354d47fd4b6e821e7 100644 (file)
@@ -39,7 +39,6 @@ if (!function_exists('versioned_asset')) {
  */
 function userCan($permission, \BookStack\Ownable $ownable = null)
 {
-    if (!auth()->check()) return false;
     if ($ownable === null) {
         return auth()->user() && auth()->user()->can($permission);
     }
index 6b273390b472ea95f54ec18be77fe2202cba5916..0be5078741b2bee2a2fe0b728a4caf8339d8a1f3 100644 (file)
@@ -21,12 +21,53 @@ class CreateEntityPermissionsTable extends Migration
             $table->boolean('has_permission')->default(false);
             $table->boolean('has_permission_own')->default(false);
             $table->integer('created_by');
+            // Create indexes
             $table->index(['entity_id', 'entity_type']);
+            $table->index('has_permission');
+            $table->index('has_permission_own');
             $table->index('role_id');
             $table->index('action');
             $table->index('created_by');
         });
 
+        Schema::table('roles', function (Blueprint $table) {
+            $table->string('system_name');
+            $table->boolean('hidden')->default(false);
+            $table->index('hidden');
+            $table->index('system_name');
+        });
+
+        // Create the new public role
+        $publicRole = new \BookStack\Role();
+        $publicRole->name = 'public';
+        $publicRole->display_name = 'Public';
+        $publicRole->description = 'The role given to public visitors if allowed';
+        $publicRole->system_name = 'public';
+        $publicRole->hidden = true;
+        // Ensure unique name
+        while (\BookStack\Role::getRole($publicRole->name) !== null) {
+            $publicRole->name = $publicRole->name . str_random(2);
+        }
+        $publicRole->save();
+
+        // Add new view permissions to public role
+        $entities = ['Book', 'Page', 'Chapter'];
+        $ops = ['View All', 'View Own'];
+        foreach ($entities as $entity) {
+            foreach ($ops as $op) {
+                $name = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op));
+                $permission = \BookStack\Permission::getByName($name);
+                // Assign view permissions to public
+                $publicRole->attachPermission($permission);
+            }
+        }
+
+        // Update admin role with system name
+        $admin = \BookStack\Role::getRole('admin');
+        $admin->system_name = 'admin';
+        $admin->save();
+
+        // Generate the new entity permissions
         $restrictionService = app(\BookStack\Services\RestrictionService::class);
         $restrictionService->buildEntityPermissions();
     }
@@ -39,5 +80,13 @@ class CreateEntityPermissionsTable extends Migration
     public function down()
     {
         Schema::drop('entity_permissions');
+
+        // Delete the public role
+        $public = \BookStack\Role::getSystemRole('public');
+        $public->delete();
+
+        Schema::table('roles', function (Blueprint $table) {
+            $table->dropColumn('system_name');
+        });
     }
 }
index b6b2d5c9761077df50d306e7a8264f79eae483ec..0bb61cebcdc6782cfc69548c9250f8c50dbd5403 100644 (file)
                     <hr>
                     <p class="text-muted">No pages are currently in this chapter.</p>
                     <p>
-                        <a href="{{$chapter->getUrl() . '/create-page'}}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a>
-                        &nbsp;&nbsp;<em class="text-muted">-or-</em>&nbsp;&nbsp;&nbsp;
-                        <a href="{{$book->getUrl() . '/sort'}}" class="text-book"><i class="zmdi zmdi-book"></i>Sort the current book</a>
+                        @if(userCan('page-create', $chapter))
+                            <a href="{{$chapter->getUrl() . '/create-page'}}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a>
+                        @endif
+                        @if(userCan('page-create', $chapter) && userCan('book-update', $book))
+                            &nbsp;&nbsp;<em class="text-muted">-or-</em>&nbsp;&nbsp;&nbsp;
+                        @endif
+                        @if(userCan('book-update', $book))
+                            <a href="{{$book->getUrl() . '/sort'}}" class="text-book"><i class="zmdi zmdi-book"></i>Sort the current book</a>
+                        @endif
                     </p>
                     <hr>
                 @endif
index 7e38154d5f00e33f25bafc325f8aefcbdac56e79..4697d34674d4d299a13aa81538cb6f76e0e72b34 100644 (file)
@@ -66,8 +66,8 @@
                 <div class="form-group">
                     <label for="setting-registration-role">Default user role after registration</label>
                     <select id="setting-registration-role" name="setting-registration-role" @if($errors->has('setting-registration-role')) class="neg" @endif>
-                        @foreach(\BookStack\Role::all() as $role)
-                            <option value="{{$role->id}}"
+                        @foreach(\BookStack\Role::visible() as $role)
+                            <option value="{{$role->id}}" data-role-name="{{ $role->name }}"
                                     @if(setting('registration-role', \BookStack\Role::first()->id) == $role->id) selected @endif
                                     >
                                 {{ $role->display_name }}
index 1594704771d8d86fa70391e1d62b1fcf9c17b411..770123cbd30f77f722b704f8961b36540e1b1b02 100644 (file)
                 </p>
                 <table class="table">
                     <tr>
-                        <th></th>
-                        <th>Create</th>
-                        <th>View</th>
-                        <th>Edit</th>
-                        <th>Delete</th>
+                        <th width="20%"></th>
+                        <th width="20%">Create</th>
+                        <th width="20%">View</th>
+                        <th width="20%">Edit</th>
+                        <th width="20%">Delete</th>
                     </tr>
                     <tr>
                         <td>Books</td>
@@ -96,6 +96,7 @@
                     <tr>
                         <td>Images</td>
                         <td>@include('settings/roles/checkbox', ['permission' => 'image-create-all'])</td>
+                        <td style="line-height:1.2;"><small class="faded">Controlled by the asset they are uploaded to</small></td>
                         <td>
                             <label>@include('settings/roles/checkbox', ['permission' => 'image-update-own']) Own</label>
                             <label>@include('settings/roles/checkbox', ['permission' => 'image-update-all']) All</label>
index 47edb211b4ce1f2b352e018676578a2a9b4b0292..5fc8ce397d10b118d7c8e6ee338aca657999c9cd 100644 (file)
@@ -13,7 +13,7 @@
 @if(userCan('users-manage'))
     <div class="form-group">
         <label for="role">User Role</label>
-        @include('form/role-checkboxes', ['name' => 'roles', 'roles' => \BookStack\Role::all()])
+        @include('form/role-checkboxes', ['name' => 'roles', 'roles' => $roles])
     </div>
 @endif
 
index 9bd70b43c4805d4671f0f626b69e30495a3e129d..52ebac9765fb52d5306471b708ac257771b9fcaf 100644 (file)
@@ -11,7 +11,7 @@
 @if(userCan('users-manage'))
     <div class="form-group">
         <label for="role">User Role</label>
-        @include('form/role-checkboxes', ['name' => 'roles', 'roles' => \BookStack\Role::all()])
+        @include('form/role-checkboxes', ['name' => 'roles', 'roles' => $roles])
     </div>
 @endif
 
index 84169c90bcc4dbdadf89b0d9a0d478be2031e6e4..b64f40dc6af325bc5ebfdbeacf8e24b60cc8c9e6 100644 (file)
@@ -544,4 +544,27 @@ class RolesTest extends TestCase
             ->dontSeeInElement('.book-content', $otherPage->name);
     }
 
+    public function test_public_role_not_visible_in_user_edit_screen()
+    {
+        $user = \BookStack\User::first();
+        $this->asAdmin()->visit('/settings/users/' . $user->id)
+            ->seeElement('#roles-admin')
+            ->dontSeeElement('#roles-public');
+    }
+
+    public function test_public_role_not_visible_in_role_listing()
+    {
+        $this->asAdmin()->visit('/settings/roles')
+            ->see('Admin')
+            ->dontSee('Public');
+    }
+
+    public function test_public_role_not_visible_in_default_role_setting()
+    {
+        $this->asAdmin()->visit('/settings')
+            ->seeElement('[data-role-name="admin"]')
+            ->dontSeeElement('[data-role-name="public"]');
+
+    }
+
 }