]> BookStack Code Mirror - bookstack/commitdiff
Refactored some permission controls and increased testing for roles system
authorDan Brown <redacted>
Wed, 2 Mar 2016 22:35:01 +0000 (22:35 +0000)
committerDan Brown <redacted>
Wed, 2 Mar 2016 22:35:01 +0000 (22:35 +0000)
app/Exceptions/PermissionsException.php [new file with mode: 0644]
app/Http/Controllers/PermissionController.php
app/Repos/PermissionsRepo.php [new file with mode: 0644]
app/Role.php
app/User.php
database/factories/ModelFactory.php
tests/RolesTest.php
tests/TestCase.php

diff --git a/app/Exceptions/PermissionsException.php b/app/Exceptions/PermissionsException.php
new file mode 100644 (file)
index 0000000..ae4c676
--- /dev/null
@@ -0,0 +1,6 @@
+<?php namespace BookStack\Exceptions;
+
+
+use Exception;
+
+class PermissionsException extends Exception {}
\ No newline at end of file
index 8cc14fc7aa32624fdc8a4d8c6e2413c0aac23822..c565bb20adc2b62a8d12f75765a3011bc0b908c0 100644 (file)
@@ -1,28 +1,22 @@
-<?php
+<?php namespace BookStack\Http\Controllers;
 
-namespace BookStack\Http\Controllers;
-
-use BookStack\Permission;
-use BookStack\Role;
+use BookStack\Exceptions\PermissionsException;
+use BookStack\Repos\PermissionsRepo;
 use Illuminate\Http\Request;
 use BookStack\Http\Requests;
 
 class PermissionController extends Controller
 {
 
-    protected $role;
-    protected $permission;
+    protected $permissionsRepo;
 
     /**
      * PermissionController constructor.
-     * @param Role $role
-     * @param Permission $permission
-     * @internal param $user
+     * @param PermissionsRepo $permissionsRepo
      */
-    public function __construct(Role $role, Permission $permission)
+    public function __construct(PermissionsRepo $permissionsRepo)
     {
-        $this->role = $role;
-        $this->permission = $permission;
+        $this->permissionsRepo = $permissionsRepo;
         parent::__construct();
     }
 
@@ -32,7 +26,7 @@ class PermissionController extends Controller
     public function listRoles()
     {
         $this->checkPermission('user-roles-manage');
-        $roles = $this->role->all();
+        $roles = $this->permissionsRepo->getAllRoles();
         return view('settings/roles/index', ['roles' => $roles]);
     }
 
@@ -59,22 +53,7 @@ class PermissionController extends Controller
             'description' => 'max:250'
         ]);
 
-        $role = $this->role->newInstance($request->all());
-        $role->name = str_replace(' ', '-', strtolower($request->get('display_name')));
-        // Prevent duplicate names
-        while ($this->role->where('name', '=', $role->name)->count() > 0) {
-            $role->name .= strtolower(str_random(2));
-        }
-        $role->save();
-
-        if ($request->has('permissions')) {
-            $permissionsNames = array_keys($request->get('permissions'));
-            $permissions = $this->permission->whereIn('name', $permissionsNames)->pluck('id')->toArray();
-            $role->permissions()->sync($permissions);
-        } else {
-            $role->permissions()->sync([]);
-        }
-
+        $this->permissionsRepo->saveNewRole($request->all());
         session()->flash('success', 'Role successfully created');
         return redirect('/settings/roles');
     }
@@ -87,7 +66,7 @@ class PermissionController extends Controller
     public function editRole($id)
     {
         $this->checkPermission('user-roles-manage');
-        $role = $this->role->findOrFail($id);
+        $role = $this->permissionsRepo->getRoleById($id);
         return view('settings/roles/edit', ['role' => $role]);
     }
 
@@ -105,24 +84,7 @@ class PermissionController extends Controller
             'description' => 'max:250'
         ]);
 
-        $role = $this->role->findOrFail($id);
-        if ($request->has('permissions')) {
-            $permissionsNames = array_keys($request->get('permissions'));
-            $permissions = $this->permission->whereIn('name', $permissionsNames)->pluck('id')->toArray();
-            $role->permissions()->sync($permissions);
-        } else {
-            $role->permissions()->sync([]);
-        }
-
-        // Ensure admin account always has all permissions
-        if ($role->name === 'admin') {
-            $permissions = $this->permission->all()->pluck('id')->toArray();
-            $role->permissions()->sync($permissions);
-        }
-
-        $role->fill($request->all());
-        $role->save();
-
+        $this->permissionsRepo->updateRole($id, $request->all());
         session()->flash('success', 'Role successfully updated');
         return redirect('/settings/roles');
     }
@@ -136,9 +98,9 @@ class PermissionController extends Controller
     public function showDeleteRole($id)
     {
         $this->checkPermission('user-roles-manage');
-        $role = $this->role->findOrFail($id);
-        $roles = $this->role->where('id', '!=', $id)->get();
-        $blankRole = $this->role->newInstance(['display_name' => 'Don\'t migrate users']);
+        $role = $this->permissionsRepo->getRoleById($id);
+        $roles = $this->permissionsRepo->getAllRolesExcept($role);
+        $blankRole = $role->newInstance(['display_name' => 'Don\'t migrate users']);
         $roles->prepend($blankRole);
         return view('settings/roles/delete', ['role' => $role, 'roles' => $roles]);
     }
@@ -153,29 +115,14 @@ class PermissionController extends Controller
     public function deleteRole($id, Request $request)
     {
         $this->checkPermission('user-roles-manage');
-        $role = $this->role->findOrFail($id);
-
-        // Prevent deleting admin role
-        if ($role->name === 'admin') {
-            session()->flash('error', 'The admin role cannot be deleted');
-            return redirect()->back();
-        }
 
-        if ($role->id == \Setting::get('registration-role')) {
-            session()->flash('error', 'This role cannot be deleted while set as the default registration role.');
+        try {
+            $this->permissionsRepo->deleteRole($id, $request->get('migrate_role_id'));
+        } catch (PermissionsException $e) {
+            session()->flash('error', $e->getMessage());
             return redirect()->back();
         }
 
-        if ($request->has('migration_role_id')) {
-            $newRole = $this->role->find($request->get('migration_role_id'));
-            if ($newRole) {
-                $users = $role->users->pluck('id')->toArray();
-                $newRole->users()->sync($users);
-            }
-        }
-
-        $role->delete();
-
         session()->flash('success', 'Role successfully deleted');
         return redirect('/settings/roles');
     }
diff --git a/app/Repos/PermissionsRepo.php b/app/Repos/PermissionsRepo.php
new file mode 100644 (file)
index 0000000..c35f29d
--- /dev/null
@@ -0,0 +1,141 @@
+<?php namespace BookStack\Repos;
+
+
+use BookStack\Exceptions\PermissionsException;
+use BookStack\Permission;
+use BookStack\Role;
+use Setting;
+
+class PermissionsRepo
+{
+
+    protected $permission;
+    protected $role;
+
+    /**
+     * PermissionsRepo constructor.
+     * @param $permission
+     * @param $role
+     */
+    public function __construct(Permission $permission, Role $role)
+    {
+        $this->permission = $permission;
+        $this->role = $role;
+    }
+
+    /**
+     * Get all the user roles from the system.
+     * @return \Illuminate\Database\Eloquent\Collection|static[]
+     */
+    public function getAllRoles()
+    {
+        return $this->role->all();
+    }
+
+    /**
+     * Get all the roles except for the provided one.
+     * @param Role $role
+     * @return mixed
+     */
+    public function getAllRolesExcept(Role $role)
+    {
+        return $this->role->where('id', '!=', $role->id)->get();
+    }
+
+    /**
+     * Get a role via its ID.
+     * @param $id
+     * @return mixed
+     */
+    public function getRoleById($id)
+    {
+        return $this->role->findOrFail($id);
+    }
+
+    /**
+     * Save a new role into the system.
+     * @param array $roleData
+     * @return Role
+     */
+    public function saveNewRole($roleData)
+    {
+        $role = $this->role->newInstance($roleData);
+        $role->name = str_replace(' ', '-', strtolower($roleData['display_name']));
+        // Prevent duplicate names
+        while ($this->role->where('name', '=', $role->name)->count() > 0) {
+            $role->name .= strtolower(str_random(2));
+        }
+        $role->save();
+
+        $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
+        $this->assignRolePermissions($role, $permissions);
+        return $role;
+    }
+
+    /**
+     * Updates an existing role.
+     * Ensure Admin role always has all permissions.
+     * @param $roleId
+     * @param $roleData
+     */
+    public function updateRole($roleId, $roleData)
+    {
+        $role = $this->role->findOrFail($roleId);
+        $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
+        $this->assignRolePermissions($role, $permissions);
+
+        if ($role->name === 'admin') {
+            $permissions = $this->permission->all()->pluck('id')->toArray();
+            $role->permissions()->sync($permissions);
+        }
+
+        $role->fill($roleData);
+        $role->save();
+    }
+
+    /**
+     * Assign an list of permission names to an role.
+     * @param Role $role
+     * @param array $permissionNameArray
+     */
+    public function assignRolePermissions(Role $role, $permissionNameArray = [])
+    {
+        $permissions = [];
+        if ($permissionNameArray && count($permissionNameArray) > 0) {
+            $permissions = $this->permission->whereIn('name', $permissionNameArray)->pluck('id')->toArray();
+        }
+        $role->permissions()->sync($permissions);
+    }
+
+    /**
+     * Delete a role from the system.
+     * Check it's not an admin role or set as default before deleting.
+     * If an migration Role ID is specified the users assign to the current role
+     * will be added to the role of the specified id.
+     * @param $roleId
+     * @param $migrateRoleId
+     * @throws PermissionsException
+     */
+    public function deleteRole($roleId, $migrateRoleId)
+    {
+        $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');
+        } else if ($role->id == Setting::get('registration-role')) {
+            throw new PermissionsException('This role cannot be deleted while set as the default registration role.');
+        }
+
+        if ($migrateRoleId) {
+            $newRole = $this->role->find($migrateRoleId);
+            if ($newRole) {
+                $users = $role->users->pluck('id')->toArray();
+                $newRole->users()->sync($users);
+            }
+        }
+
+        $role->delete();
+    }
+
+}
\ No newline at end of file
index 8d5ed7d6678e4266ff94e8666150cd13d0d32c7b..270e4e0b8e32ce1287be95dae0d1c138d8120e3b 100644 (file)
@@ -8,11 +8,6 @@ class Role extends Model
 {
 
     protected $fillable = ['display_name', 'description'];
-    /**
-     * Sets the default role name for newly registered users.
-     * @var string
-     */
-    protected static $default = 'viewer';
 
     /**
      * The roles that belong to the role.
@@ -48,15 +43,6 @@ class Role extends Model
         $this->permissions()->attach($permission->id);
     }
 
-    /**
-     * Get an instance of the default role.
-     * @return Role
-     */
-    public static function getDefault()
-    {
-        return static::getRole(static::$default);
-    }
-
     /**
      * Get the role object for the specified role.
      * @param $roleName
index b062aa78f20dc11a05409132c82547c2ba57b12a..2d14c6e6e8a8588c8496818c9ee1b89641af6f55 100644 (file)
@@ -106,7 +106,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
      */
     public function attachRoleId($id)
     {
-        $this->roles()->sync([$id]);
+        $this->roles()->attach([$id]);
     }
 
     /**
index e0f1550875c27a6e0ec0710a3493f23ca1e097f6..2840356e87198633213a700cec46109cdb494f1a 100644 (file)
@@ -17,6 +17,7 @@ $factory->define(BookStack\User::class, function ($faker) {
         'email' => $faker->email,
         'password' => str_random(10),
         'remember_token' => str_random(10),
+        'email_confirmed' => 1
     ];
 });
 
@@ -45,3 +46,10 @@ $factory->define(BookStack\Page::class, function ($faker) {
         'text' => strip_tags($html)
     ];
 });
+
+$factory->define(BookStack\Role::class, function ($faker) {
+    return [
+        'display_name' => $faker->sentence(3),
+        'description' => $faker->sentence(10)
+    ];
+});
\ No newline at end of file
index 140290bead6092ffa0d6081d9abd50419ac9905d..7349c29682be833e8311e77eca94897b597f0207 100644 (file)
@@ -9,13 +9,14 @@ class RolesTest extends TestCase
         parent::setUp();
     }
 
+    /**
+     * Create a new basic role for testing purposes.
+     * @return static
+     */
     protected function createNewRole()
     {
-        return \BookStack\Role::forceCreate([
-            'name' => 'test-role',
-            'display_name' => 'Test Role',
-            'description' => 'This is a role for testing'
-        ]);
+        $permissionRepo = app('BookStack\Repos\PermissionsRepo');
+        return $permissionRepo->saveNewRole(factory(\BookStack\Role::class)->make()->toArray());
     }
 
     public function test_admin_can_see_settings()
@@ -45,4 +46,38 @@ class RolesTest extends TestCase
             ->see('cannot be deleted');
     }
 
+    public function test_role_create_update_delete_flow()
+    {
+        $testRoleName = 'Test Role';
+        $testRoleDesc = 'a little test description';
+        $testRoleUpdateName = 'An Super Updated role';
+
+        // Creation
+        $this->asAdmin()->visit('/settings')
+            ->click('Roles')
+            ->seePageIs('/settings/roles')
+            ->click('Add new role')
+            ->type('Test Role', 'display_name')
+            ->type('A little test description', 'description')
+            ->press('Save Role')
+            ->seeInDatabase('roles', ['display_name' => $testRoleName, 'name' => 'test-role', 'description' => $testRoleDesc])
+            ->seePageIs('/settings/roles');
+        // Updating
+        $this->asAdmin()->visit('/settings/roles')
+            ->see($testRoleDesc)
+            ->click($testRoleName)
+            ->type($testRoleUpdateName, '#display_name')
+            ->press('Save Role')
+            ->seeInDatabase('roles', ['display_name' => $testRoleUpdateName, 'name' => 'test-role', 'description' => $testRoleDesc])
+            ->seePageIs('/settings/roles');
+        // Deleting
+        $this->asAdmin()->visit('/settings/roles')
+            ->click($testRoleUpdateName)
+            ->click('Delete Role')
+            ->see($testRoleUpdateName)
+            ->press('Confirm')
+            ->seePageIs('/settings/roles')
+            ->dontSee($testRoleUpdateName);
+    }
+
 }
index ce4e93b122864dd266b6b60bee375ec3ca88f6f3..a521fd076ad9cee58c03a3e0db1b134ae5c652cd 100644 (file)
@@ -79,8 +79,8 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
     protected function getNewUser($attributes = [])
     {
         $user = factory(\BookStack\User::class)->create($attributes);
-        $userRepo = app('BookStack\Repos\UserRepo');
-        $userRepo->attachDefaultRole($user);
+        $role = \BookStack\Role::getRole('editor');
+        $user->attachRole($role);;
         return $user;
     }
 
Morty Proxy This is a proxified and sanitized view of the page, visit original site.